<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>BigConfig | Blog</title><description/><link>https://www.bigconfig.it/</link><language>en</language><item><title>Terraform Workspaces Are Not Environments — BigConfig Makes It a Non-Issue</title><link>https://www.bigconfig.it/blog/terraform-workspaces-are-not-environments-bigconfig-makes-it-a-non-issue/</link><guid isPermaLink="true">https://www.bigconfig.it/blog/terraform-workspaces-are-not-environments-bigconfig-makes-it-a-non-issue/</guid><description>A recurring LinkedIn debate: should you use Terraform workspaces, separate root modules, or copy-pasted folders to isolate environments? Every answer is a tradeoff between WET code, sprawl, and blast radius. This post argues the debate itself is the symptom — Terraform fuses its unit of reuse (the module) with its unit of deployment (the root module + state). BigConfig separates them, treating environments as values passed to packages, and the whole class of problem disappears.

</description><pubDate>Sun, 26 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A thread came across my LinkedIn feed this week. The setup: someone pointed out that Terraform docs themselves say workspaces aren’t meant for environment isolation. The replies were the usual matrix of “well, actually” — separate state files, separate root modules per env, separate provider configs, separate pipelines, the WET-vs-DRY tug of war, the “we migrated 14 workspaces to isolated root modules last year” war stories.&lt;/p&gt;
&lt;p&gt;Reading it, I had the same reaction I always have to these threads: the dichotomy itself is the tell.&lt;/p&gt;
&lt;p&gt;“Workspaces vs separate root modules.” “WET vs sprawl.” “Shared state vs blast radius.” These aren’t real architectural choices. They’re symptoms of Terraform forcing you to pick your poison at the wrong layer.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;why-the-debate-keeps-happening&quot;&gt;Why the debate keeps happening&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Terraform fuses two things that should be independent:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The unit of reuse&lt;/strong&gt; — the module. A module is how you avoid copy-pasting resource definitions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The unit of deployment&lt;/strong&gt; — the root module plus its state file. This is what &lt;code dir=&quot;auto&quot;&gt;terraform apply&lt;/code&gt; actually operates on, and what owns blast radius, IAM, and provider config.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Because these are fused, every “how do I isolate environments?” question becomes a folder-layout question. Do I have one root module with workspaces? One root module per env? A wrapper directory per env that calls a shared module? Each answer trades one form of pain for another:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Workspaces&lt;/strong&gt; → DRY, but shared backend, shared providers, shared blast radius. The docs themselves warn you off.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;One root module per env&lt;/strong&gt; → strong isolation, but now you’re maintaining N nearly-identical folders. Drift is inevitable. Reviewers can’t tell what actually changed across envs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Wrapper directories calling a shared module&lt;/strong&gt; → better, but you’re still hand-rolling the parameterization, the pipeline plumbing, and the promotion story. And &lt;code dir=&quot;auto&quot;&gt;count = var.env == &quot;prd&quot; ? 1 : 0&lt;/code&gt; conditionals start showing up.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is why the same thread gets re-litigated on LinkedIn every few months. There is no right answer inside Terraform’s model.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;what-bigconfig-does-differently&quot;&gt;What BigConfig does differently&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;In BigConfig, &lt;strong&gt;environments are values, not directories.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;An environment is a map of inputs passed to a package. The same package — fully reusable, version-pinned, DRY — gets instantiated N times with different inputs. Each instantiation gets its own state, its own provider config, its own IAM boundary, its own pipeline gate. Automatically. By default.&lt;/p&gt;
&lt;p&gt;There is no workspace flag. There are no copy-pasted root modules. There are no &lt;code dir=&quot;auto&quot;&gt;count = var.env == &quot;prd&quot; ? 1 : 0&lt;/code&gt; conditionals rotting in the corner of a file no one wants to touch.&lt;/p&gt;
&lt;p&gt;What that buys you in practice:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;DRY without sprawl.&lt;/strong&gt; One package definition, parameterized. Not 14 root modules to keep in sync.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Strong isolation by default.&lt;/strong&gt; Separate state and credentials per instance is the &lt;em&gt;default&lt;/em&gt;, not a discipline you have to enforce in code review.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Composable.&lt;/strong&gt; Environments are built from packages the same way packages are built from resources. Promotion = bumping a version, not diffing two folders.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multi-instance is free.&lt;/strong&gt; “What about per-tenant, per-region, per-cell?” stops being an architecture decision and becomes a &lt;code dir=&quot;auto&quot;&gt;for&lt;/code&gt; loop.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;the-deeper-point&quot;&gt;The deeper point&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The Terraform community keeps having this argument because Terraform’s unit of reuse and its unit of deployment are the same artifact. Every workaround — workspaces, Terragrunt wrappers, generated HCL, monorepos of near-duplicate folders — is an attempt to pry them apart from the outside.&lt;/p&gt;
&lt;p&gt;BigConfig pries them apart from the inside. A package describes &lt;em&gt;what&lt;/em&gt; you want. An instantiation describes &lt;em&gt;where&lt;/em&gt; and &lt;em&gt;with what inputs&lt;/em&gt;. State, providers, and IAM follow the instantiation, not the package.&lt;/p&gt;
&lt;p&gt;Once you have that separation, “workspaces aren’t environments” stops being a hot take and becomes a non-issue. Same for most of the other recurring DevOps debates — they’re not wrong arguments, they’re just arguments that only exist because the underlying tool conflated two concerns.&lt;/p&gt;
&lt;p&gt;To see how your agent can operate a BigConfig package that encapsulates multiple Terraform resources and Ansible playbooks, start at the &lt;a href=&quot;https://bigconfig.it/#demo&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; BigConfig Demo &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;.&lt;/p&gt;</content:encoded><category>bigconfig</category><category>terraform</category><category>devops</category><category>iac</category><category>platform-engineering</category><category>cloud-architecture</category></item><item><title>Why Agents Need an Infrastructure Package Manager</title><link>https://www.bigconfig.it/blog/why-agents-need-an-infrastructure-package-manager/</link><guid isPermaLink="true">https://www.bigconfig.it/blog/why-agents-need-an-infrastructure-package-manager/</guid><description>Kief Morris at infrastructure-as-code.com nails it: having agents write reusable infrastructure code is far safer than letting them drive Terraform directly. But the ecosystem of composable components he envisions already exists—it&apos;s called BigConfig.

In this reply, we explore how BigConfig&apos;s package manager model gives agents exactly the high-level abstraction layer they need to go from &quot;messy tourist&quot; to reliable infrastructure composer.

</description><pubDate>Sun, 26 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Kief Morris recently published &lt;a href=&quot;https://infrastructure-as-code.com/why-i-use-infra-code-with-agents.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Why I have my agents write infrastructure code &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;, and it is one of the clearest articulations of Agentic DevOps I have read. His central observation is hard to argue with: when you let an agent drive the AWS CLI directly, it takes multiple tries, casually blows away data, and replaces solid implementations with slow, less-reliable alternatives. But when you ask an agent to &lt;em&gt;write&lt;/em&gt; reusable infrastructure code instead of &lt;em&gt;executing&lt;/em&gt; low level CLI tools, you get something much safer: a composable, auditable artifact that a human can review before anything touches production.&lt;/p&gt;
&lt;p&gt;We agree with everything in that post. In fact, we have been building exactly the ecosystem he envisions—we just call it a &lt;strong&gt;package manager&lt;/strong&gt;.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-gap-between-intent-and-execution&quot;&gt;The Gap Between Intent and Execution&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Morris frames the problem as an agent adapting pre-existing components to specific needs. The missing piece, as he notes, is the ecosystem of components itself. Without it, agents fall back to generating raw HCL or shell scripts from scratch, which is precisely where hallucinations and cascading failures live.&lt;/p&gt;
&lt;p&gt;This is the same gap the web ecosystem solved between 2010 and 2015. Before React and npm, every JavaScript developer rewrote the same DOM manipulation logic from scratch. After React, the ecosystem converged on components. A developer—or today, an agent—doesn’t write a date-picker from scratch; they install one from a registry and wire it into the application.&lt;/p&gt;
&lt;p&gt;DevOps needs the same shift. BigConfig is the attempt to make it happen.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;bigconfig-as-the-npm-for-infrastructure&quot;&gt;BigConfig as the npm for Infrastructure&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;BigConfig is a Clojure-based infrastructure package manager. The mental model is intentionally close to npm or Maven:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Packages&lt;/strong&gt; are versioned, composable units of infrastructure logic (compute, DNS, SMTP, Kubernetes controllers, etc.)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The lock file&lt;/strong&gt; records the exact versions resolved, so every &lt;code dir=&quot;auto&quot;&gt;bb bigconfig apply&lt;/code&gt; is deterministic&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Babashka&lt;/strong&gt; executes the packages locally—no server, no daemon, no state machine to manage&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When an agent works with BigConfig, it is not guessing Terraform resource attributes or wrestling with provider schema drift. It is composing packages the same way a React developer composes components: declare what you want, let the abstraction handle the how.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;bb.edn&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;:deps&lt;/span&gt;&lt;span&gt;  {io.github.amiorin/once {&lt;/span&gt;&lt;span&gt;:git/url&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;https://github.com/amiorin/once&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                 &lt;/span&gt;&lt;span&gt;:git/sha&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;8ffbbc2ea0974365575c7ee44b7d890e69447144&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;}}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:tasks&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt;:requires&lt;/span&gt;&lt;span&gt; ([io.github.amiorin.once.package &lt;/span&gt;&lt;span&gt;:as&lt;/span&gt;&lt;span&gt; pkg])&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;package   {&lt;/span&gt;&lt;span&gt;:doc&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;bb package create | bb package delete&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                    &lt;/span&gt;&lt;span&gt;:task&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;pkg/once*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                           &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*command-line-args*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                           &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;:big-config.render/profile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;online&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                            &lt;/span&gt;&lt;span&gt;:big-config.workflow/params&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;:domain&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;bigconfig.online&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                             &lt;/span&gt;&lt;span&gt;:package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;online&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                             &lt;/span&gt;&lt;span&gt;:once&lt;/span&gt;&lt;span&gt;    {&lt;/span&gt;&lt;span&gt;:applications&lt;/span&gt;&lt;span&gt; [{&lt;/span&gt;&lt;span&gt;:host&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;www.bigconfig.online&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                                       &lt;/span&gt;&lt;span&gt;:image&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;ghcr.io/amiorin/big-config-website:latest&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;}]}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                             &lt;/span&gt;&lt;span&gt;:provider-compute&lt;/span&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;oci&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                             &lt;/span&gt;&lt;span&gt;:oci-config-file-profile&lt;/span&gt;&lt;span&gt;     &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;DEFAULT&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                             &lt;/span&gt;&lt;span&gt;:oci-display-name&lt;/span&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;bigconfig-online&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                             &lt;/span&gt;&lt;span&gt;:oci-shape&lt;/span&gt;&lt;span&gt;                   &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;VM.Standard.A1.Flex&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                             &lt;/span&gt;&lt;span&gt;:oci-ocpus&lt;/span&gt;&lt;span&gt;                   &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                             &lt;/span&gt;&lt;span&gt;:oci-memory-in-gbs&lt;/span&gt;&lt;span&gt;           &lt;/span&gt;&lt;span&gt;6&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                             &lt;/span&gt;&lt;span&gt;:oci-boot-volume-size-in-gbs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;50&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                             &lt;/span&gt;&lt;span&gt;:oci-boot-volume-vpus-per-gb&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;30&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                             &lt;/span&gt;&lt;span&gt;:oci-ssh-authorized-keys&lt;/span&gt;&lt;span&gt;     &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;~/.ssh/id_ed25519.pub&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                             &lt;/span&gt;&lt;span&gt;:provider-smtp&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;resend&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                             &lt;/span&gt;&lt;span&gt;:resend-server&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;smtp.resend.com&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                             &lt;/span&gt;&lt;span&gt;:resend-port&lt;/span&gt;&lt;span&gt;     &lt;/span&gt;&lt;span&gt;587&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                             &lt;/span&gt;&lt;span&gt;:resend-username&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;resend&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                             &lt;/span&gt;&lt;span&gt;:provider-dns&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;cloudflare&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                             &lt;/span&gt;&lt;span&gt;:provider-backend&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;s3&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;}})}}}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;An agent reading this file understands the intent immediately. It does not need to know the Hetzner API, the Cloudflare zone API, or the Resend domain verification flow. Those details are encapsulated inside the package.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;why-clojure&quot;&gt;Why Clojure?&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Morris notes that unlike static libraries, agents can adapt code to specific needs. This is where the language matters. Clojure’s data-driven programming model means that infrastructure packages are, at their core, plain data maps. An agent can read them, modify them, and reason about them without parsing a DSL or reverse-engineering a class hierarchy. The code that acts on the data is small and testable independently.&lt;/p&gt;
&lt;p&gt;Babashka—the scripting runtime we use—runs the same Clojure source without a JVM startup penalty, which means an agent can invoke &lt;code dir=&quot;auto&quot;&gt;bb package create&lt;/code&gt; as naturally as it runs &lt;code dir=&quot;auto&quot;&gt;terraform apply&lt;/code&gt;, but with far more predictable outcomes.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;reusability-without-rigidity&quot;&gt;Reusability Without Rigidity&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;One concern Morris raises is the tension between reusability and adaptability. A static library forces you into its abstractions; you fight it the moment your requirements diverge. BigConfig avoids this by treating packages as &lt;strong&gt;data transformations&lt;/strong&gt; rather than black-box executables. Each package exposes its intermediate data before it runs, so an agent—or a human—can inspect and override specific values without forking the package.&lt;/p&gt;
&lt;p&gt;This is the same principle as React’s props. You do not rewrite the component; you pass different props. In BigConfig, you pass different configuration keys.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-ecosystem-is-already-growing&quot;&gt;The Ecosystem Is Already Growing&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/amiorin/once&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; once &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; package is the first published example: a complete personal PaaS (compute + DNS + SMTP) that an agent can provision on Hetzner or OCI with a single command. More packages are in progress for Kubernetes namespaces, observability stacks, and database clusters.&lt;/p&gt;
&lt;p&gt;Each new package is a node in the ecosystem Morris envisions. Each one reduces the surface area an agent must hallucinate, and increases the confidence that what gets applied to production is what was intended.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;what-this-means-for-practitioners&quot;&gt;What This Means for Practitioners&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Morris’s post ends with a hopeful note: if agents can adapt working, pre-built code to specific needs, we may have the basis for a healthy ecosystem of infrastructure components. We share that hope, and we think the path there is clear:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Stop asking agents to write raw Terraform.&lt;/strong&gt; They are being asked to generate “assembly code” for infrastructure, and they will keep making assembly-level mistakes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Build or adopt a package layer.&lt;/strong&gt; The package is the unit of trust, not the generated file.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Let the agent compose, not author.&lt;/strong&gt; The moment the agent’s job becomes “pick the right packages and supply the right configuration,” the error rate drops dramatically.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you are thinking about how to structure infrastructure for an agentic world, the package manager mental model is worth exploring. BigConfig is one implementation of that idea, built in Clojure with Babashka, and it is open source.&lt;/p&gt;
&lt;p&gt;To see how your agent can operate a BigConfig package that encapsulates multiple Terraform resources and Ansible playbooks, start at the &lt;a href=&quot;https://bigconfig.it/#demo&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; BigConfig Demo &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;.&lt;/p&gt;</content:encoded><category>bigconfig</category><category>package manager</category><category>abstraction</category><category>agents</category><category>terraform</category><category>clojure</category><category>babashka</category><category>vibe operations</category></item><item><title>Do you trust your DevOps code as much as you trust Postgres?</title><link>https://www.bigconfig.it/blog/do-you-trust-your-devops-code-as-much-as-you-trust-postgres/</link><guid isPermaLink="true">https://www.bigconfig.it/blog/do-you-trust-your-devops-code-as-much-as-you-trust-postgres/</guid><description>Postgres is trusted because it is hardened by decades of production use. Most Terraform code is not. Here is why BigConfig packages aim for &quot;Postgres-level&quot; reliability — and a real incident that shows why it matters.

</description><pubDate>Fri, 24 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;You shouldn’t. Most DevOps teams write Terraform from scratch, yet no Backend team would try to write their own database engine. Postgres is reliable because it has been hardened by decades of production workloads, across every imaginable failure mode.&lt;/p&gt;
&lt;p&gt;In the age of AI agents, we are generating more Terraform code than ever. But without a standardized foundation, that code is “alpha” — as risky as using a database that was just uploaded to GitHub yesterday.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-problem-the-bespoke-platform-trap&quot;&gt;The problem: the “bespoke platform” trap&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;In operations, we are all solving the same fundamental problems. Yet, unlike the database world, every company “rolls their own” platform. This leads to two outcomes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Constant babysitting:&lt;/strong&gt; high-maintenance scripts that require manual intervention.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Low reliability:&lt;/strong&gt; brittle infrastructure that breaks under edge cases.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;BigConfig is changing this.&lt;/strong&gt; We are building infrastructure packages that earn your trust through mass adoption. A BigConfig package isn’t just a script; it’s a hardened standard, deployed so many times it reaches the “Postgres level” of reliability.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;a-real-world-lesson-the-latest-trap&quot;&gt;A real-world lesson: the “latest” trap&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Even with years of Terraform experience, I recently faced unplanned downtime due to a common mistake. My compute instance was set to pull the “latest” image. When Oracle released a new Ubuntu version, Terraform triggered a recreation of the VPS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The fix?&lt;/strong&gt; I had to refactor the resource to ignore changes to the source image, using &lt;a href=&quot;https://developer.hashicorp.com/terraform/language/meta-arguments/lifecycle&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Terraform’s &lt;code dir=&quot;auto&quot;&gt;lifecycle&lt;/code&gt; meta-argument &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;lifecycle {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;prevent_destroy &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;{ compute&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;prevent&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;destroy }&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;&quot;&gt;}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;That single block hands control of the maintenance window back to the operator, instead of letting an upstream image publish silently destroy the VM.&lt;/p&gt;
&lt;p&gt;This is exactly how databases work — even AWS RDS requires a defined maintenance window for upgrades. Infrastructure should be no different. BigConfig bakes these hard-learned lessons into every package, so you don’t have to learn them the hard way.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-takeaway&quot;&gt;The takeaway&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;So, do you trust your DevOps code as much as you trust Postgres? If the honest answer is “no,” the problem isn’t your team — it’s that every team is rewriting the same primitives from scratch. Shared, battle-tested packages are the only way infrastructure code earns the kind of trust we already give our databases.&lt;/p&gt;
&lt;p&gt;Stop writing infrastructure from scratch. Start from a package that’s already been hardened in production.&lt;/p&gt;
&lt;div&gt; &lt;span&gt; &lt;a href=&quot;https://www.bigconfig.it/marketplace&quot;&gt; &lt;span&gt;Browse the BigConfig Marketplace&lt;/span&gt; &lt;/a&gt;  &lt;/span&gt; &lt;svg aria-hidden=&quot;true&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot;&gt;&lt;path d=&quot;M17.92 11.62a1.001 1.001 0 0 0-.21-.33l-5-5a1.003 1.003 0 1 0-1.42 1.42l3.3 3.29H7a1 1 0 0 0 0 2h7.59l-3.3 3.29a1.002 1.002 0 0 0 .325 1.639 1 1 0 0 0 1.095-.219l5-5a1 1 0 0 0 .21-.33 1 1 0 0 0 0-.76Z&quot; /&gt;&lt;/svg&gt; &lt;/div&gt; </content:encoded><category>bigconfig</category><category>terraform</category><category>package manager</category><category>ai-agents</category><category>gitops</category><category>marketplace</category></item><item><title>The Future of Software Distribution: How LLMs are Challenging the SaaS Dominance</title><link>https://www.bigconfig.it/blog/the-future-of-software-distribution-how-llms-are-challenging-the-saas-dominance/</link><guid isPermaLink="true">https://www.bigconfig.it/blog/the-future-of-software-distribution-how-llms-are-challenging-the-saas-dominance/</guid><description>The SaaS era assumed running software yourself was too expensive. AI agents are changing that math — and with it, the case for licensed, self-hosted software over subscriptions.

</description><pubDate>Wed, 22 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;For a long time, the software industry has operated under the assumption that Software as a Service (SaaS) is the undisputed king of software distribution. But this long-held belief is being called into question by the rapid rise of Large Language Models (LLMs).&lt;/p&gt;
&lt;p&gt;The classic “buy versus build” dilemma for companies is becoming more complex. In the SaaS era, the choice was often simple: buy if you don’t want to operate it, self-host if you do. Now, LLMs and AI agents are making self-hosting more attractive and cost-effective.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-snowflake-example-and-the-return-to-licensing&quot;&gt;The Snowflake Example and the Return to Licensing&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Take &lt;a href=&quot;https://www.snowflake.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Snowflake &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;, arguably one of the most successful SaaS products. It displaced traditional, license-based data warehouses like Teradata and Vertica with a closed-source SaaS model. However, we may be on the verge of a trend reversal.&lt;/p&gt;
&lt;p&gt;In the future, a software license for a product like Snowflake could become more appealing than a SaaS subscription for several reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Data Sovereignty:&lt;/strong&gt; Paying for a license allows a company to keep its data within its own country because it maintains control over the hardware.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compliance and Security:&lt;/strong&gt; Navigating regulatory requirements can be easier with on-premises or licensed software.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Simplified Legalities:&lt;/strong&gt; Companies may be able to avoid complex data processing agreements.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;the-role-of-the-platform-team-and-ai-agents&quot;&gt;The Role of the Platform Team and AI Agents&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Implementing SaaS in large organizations is rarely a “plug-and-play” experience. It typically requires a dedicated internal team to act as a bridge between the company’s requirements and the SaaS provider. This team handles onboarding, ticket management, compliance, and cost optimization.&lt;/p&gt;
&lt;p&gt;As AI agents begin to automate these tasks and even the management of hardware, the burden of running software in-house significantly decreases. Picture an agent that provisions a Postgres cluster, monitors replication lag, applies security patches during a maintenance window, rolls back a failed upgrade, and files a postmortem ticket — all without a human on call. The operational tax that once justified the SaaS premium shrinks toward zero. Since large companies are already equipped to run their own software, the transition back to licensed models, supported by AI, becomes a much smaller hurdle.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-open-source-illusion-and-the-shift-in-monetization&quot;&gt;The Open-Source Illusion and the Shift in Monetization&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The relationship between open-source and SaaS is also evolving. Many companies have used open-source software as a “funnel”, building a community of users who eventually become paying customers for a proprietary SaaS version. This strategy has focused on growth over immediate profitability.&lt;/p&gt;
&lt;p&gt;However, the LLM era is disrupting this cycle. Investors and companies are starting to doubt whether they can monetize these large user bases in the future. As AI companies like &lt;a href=&quot;https://openai.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; OpenAI &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; and &lt;a href=&quot;https://www.anthropic.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Anthropic &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; begin to capture more of the market with their tokens and agents, the traditional SaaS margin is being cannibalized.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;a-return-to-proprietary-licensed-software&quot;&gt;A Return to Proprietary, Licensed Software?&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;We may be heading toward a future where:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Open-source’s role is redefined:&lt;/strong&gt; Its synergy with SaaS is weakening as AI changes the value proposition.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Proprietary software returns:&lt;/strong&gt; Users might download software for free for limited use (e.g., one computer or one project) but pay a license fee for larger deployments.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The database remains closed-source:&lt;/strong&gt; While the software to manage a database might be open-source, the database engine itself — which is much harder to replicate — will likely remain closed-source and license-based.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this shifting landscape, competition among token providers will likely remain high, which is good news for the industry. But the SaaS model of software distribution will have to adapt.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;where-bigconfig-fits&quot;&gt;Where BigConfig Fits&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;This is the world BigConfig is built for. If AI agents make self-hosting cheap, the missing piece is a way to &lt;em&gt;describe&lt;/em&gt; infrastructure declaratively enough that agents — not humans — can operate it. Take &lt;a href=&quot;https://www.bigconfig.it/walter&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Walter &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;, a BigConfig package for cloud development environments that competes directly with SaaS offerings like &lt;a href=&quot;https://cloud.google.com/workstations&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Google Cloud Workstations &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;. Instead of paying a per-seat subscription, you install Walter on your own infrastructure and let an agent handle provisioning, lifecycle, and cleanup. The community writes the package; you run it on your own hardware; the SaaS premium disappears and the control stays with you.&lt;/p&gt;</content:encoded><category>saas</category><category>llm</category><category>ai-agents</category><category>open-source</category><category>licensing</category><category>bigconfig</category><category>walter</category><category>cloud dev environment</category></item><item><title>Your GitHub Actions Workflow is a Waste of Time</title><link>https://www.bigconfig.it/blog/your-github-actions-workflow-is-a-waste-of-time/</link><guid isPermaLink="true">https://www.bigconfig.it/blog/your-github-actions-workflow-is-a-waste-of-time/</guid><description>Stop treating your CI as a separate, fragile ecosystem that needs its own set of rules. Most developers waste hours debugging &quot;it works on my machine&quot; issues because their GitHub Actions workflows are trying to solve provisioning and caching problems that have already been solved in development. By shifting to self-hosted runners and leveraging environment managers like Devenv, you can achieve perfect parity between your local terminal and your CI, turning a complex YAML headache into a simple, lightning-fast 24-second feedback loop.

</description><pubDate>Tue, 21 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Over the years, I’ve experimented with almost every flavor of development environment. I’ve gone from manually provisioning tools on a Mac—hoping I’d remember every &lt;code dir=&quot;auto&quot;&gt;brew install&lt;/code&gt; six months later—to exploring Docker, Nix, and remote environments.&lt;/p&gt;
&lt;p&gt;My journey has touched it all: &lt;strong&gt;asdf&lt;/strong&gt;, &lt;strong&gt;Brew&lt;/strong&gt;, &lt;strong&gt;Docker&lt;/strong&gt;, &lt;strong&gt;Nix&lt;/strong&gt;, and &lt;strong&gt;Devbox&lt;/strong&gt;. I’ve jumped between terminal emulators and multiplexers like &lt;strong&gt;Tmux&lt;/strong&gt;, &lt;strong&gt;Kitty&lt;/strong&gt;, &lt;strong&gt;WezTerm&lt;/strong&gt;, and &lt;strong&gt;Zellij&lt;/strong&gt;.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;my-modern-development-environment&quot;&gt;My Modern Development Environment&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;My latest setup is built for speed, reproducibility, and a “keyboard-first” philosophy. It lives entirely in the terminal across two environments: my local &lt;strong&gt;iMac&lt;/strong&gt; and an &lt;strong&gt;OCI Ampere VPS&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Terminal:&lt;/strong&gt; Ghostty&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multiplexer:&lt;/strong&gt; Zellij&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Environment Management:&lt;/strong&gt; Devenv&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Editor:&lt;/strong&gt; Doom Emacs&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;achieving-ci-parity-with-self-hosted-runners&quot;&gt;Achieving CI Parity with Self-Hosted Runners&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;If you haven’t switched to a &lt;strong&gt;self-hosted GitHub runner&lt;/strong&gt; yet, do it for your own sanity. You can thank me later.&lt;/p&gt;
&lt;p&gt;By running your CI on your own hardware (like an OCI Ampere instance), you eliminate the overhead of public runners and gain full control over the environment. When paired with &lt;strong&gt;Devenv&lt;/strong&gt;, your CI environment becomes an exact mirror of your local machine.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;how-to-set-it-up&quot;&gt;How to set it up:&lt;/h3&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Navigate to your GitHub repository &lt;strong&gt;Settings&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;Actions&lt;/strong&gt; -&gt; &lt;strong&gt;Runners&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;New self-hosted runner&lt;/strong&gt; and follow the configuration steps for your OS.&lt;/li&gt;
&lt;li&gt;Update your workflow &lt;code dir=&quot;auto&quot;&gt;.yml&lt;/code&gt; file to use the &lt;code dir=&quot;auto&quot;&gt;self-hosted&lt;/code&gt; label.&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;h3 id=&quot;quantifying-the-impact-on-feedback-loops&quot;&gt;Quantifying the Impact on Feedback Loops&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;By moving to a self-hosted ARM64 runner, my feedback loop became incredibly tight. My tests now finish in &lt;strong&gt;24 seconds&lt;/strong&gt;, and the entire image creation process takes just &lt;strong&gt;1 minute and 22 seconds&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Here is what the streamlined job looks like:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;jobs&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;test&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;runs-on&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;self-hosted&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;Linux&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;ARM64&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;steps&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;uses&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;actions/checkout@v5&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;with&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;fetch-depth&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Run tests&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;run_tests&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;devenv shell clojure -X:test&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;the-power-of-declarative-environments&quot;&gt;The Power of Declarative Environments&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Because I’m using &lt;code dir=&quot;auto&quot;&gt;devenv shell&lt;/code&gt;, I don’t have to worry about whether the CI runner has Clojure, the right JDK, or specific libraries installed. If it works in my local terminal, it works in the CI. Period.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;final-thoughts-simplify-your-workflow&quot;&gt;Final Thoughts: Simplify Your Workflow&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The way we use GitHub Actions today is often redundant. We spend an enormous amount of time writing complex YAML configurations to install dependencies, manage versions, and configure caching—essentially re-architecting our entire development environment for every single commit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Provisioning and caching are solved problems.&lt;/strong&gt; If you are using tools like &lt;strong&gt;Devenv&lt;/strong&gt; or &lt;strong&gt;Nix&lt;/strong&gt;, you’ve already defined exactly what your project needs to run. By moving to a &lt;strong&gt;self-hosted runner&lt;/strong&gt;, you stop fighting the CI and start using it as a natural extension of your workstation. You gain:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Total Parity:&lt;/strong&gt; If the code runs in your local &lt;code dir=&quot;auto&quot;&gt;devenv shell&lt;/code&gt;, it will run in CI. No exceptions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Instant Caching:&lt;/strong&gt; Since the runner is persistent, you don’t need to upload or download massive cache blobs; the dependencies are already there.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Minimal Configuration:&lt;/strong&gt; Your workflow files shrink from dozens of lines of setup boilerplate to a single command.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It’s time to stop treating CI like a special snowflake and start treating it like the high-performance terminal it should be. Stop provisioning twice, stop waiting for public runners, and start shipping faster.&lt;/p&gt;
&lt;p&gt;Would you like to have a follow-up on this topic? What are your thoughts? I’d love to &lt;a href=&quot;https://www.albertomiorin.com/contact#form&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; hear &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; your experiences.&lt;/p&gt;</content:encoded><category>ci</category><category>cd</category><category>github</category><category>github self-hosted runner</category><category>remote development environment</category></item><item><title>AI Speaks Terraform Like a Tourist</title><link>https://www.bigconfig.it/blog/ai-speaks-terraform-like-a-tourist/</link><guid isPermaLink="true">https://www.bigconfig.it/blog/ai-speaks-terraform-like-a-tourist/</guid><description>Is AI really just a &quot;tourist&quot; when it comes to Infrastructure as Code? While many DevOps practitioners find comfort in AI&apos;s current struggles with complex Terraform repositories, this article argues we are misdiagnosing the problem. By comparing our current DevOps landscape to the &quot;pre-React&quot; era of web development, we explore why a lack of abstraction—not a lack of intelligence—is holding AI back. Discover why the shift toward high-level abstractions like BigConfig is turning AI from a fumbling tourist into a superhuman, and what this means for the future of your career.

</description><pubDate>Tue, 14 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I recently stumbled upon a promoted post for IaCConf 2026 that gave me pause. The headline was claiming that “AI Speaks Terraform Like a Tourist”.&lt;/p&gt;
&lt;p&gt;It’s a comforting thought for many, but it gave me cold feet. Seeing “1,000s of IaC practitioners” gathering to discuss this makes me wonder: How many of them will still easily find a new job in a year?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.bigconfig.it/_astro/promoted.hpiYtnEV_1QeAKq.webp&quot; alt=&quot;promoted&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;1170&quot; height=&quot;1501&quot;&gt;&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-pre-react-era-of-devops&quot;&gt;The “Pre-React” Era of DevOps&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;We often find comfort in the fact that AI currently struggles to maintain complex Terraform repositories. We assume the models just aren’t “smart” enough yet. But I think we’re misdiagnosing the problem.&lt;/p&gt;
&lt;p&gt;The struggle isn’t a lack of AI intelligence; it’s a lack of abstraction.&lt;/p&gt;
&lt;p&gt;DevOps today is stuck in its own “pre-React” era. Think back to early web development: we separated code by technology—a folder for JS, one for HTML, and one for CSS. Then React arrived, introducing components and encapsulation. Suddenly, the “how” was hidden behind a functional “what”.&lt;/p&gt;
&lt;p&gt;Current DevOps often practices separation by technology instead of concerns. This creates a lack of abstraction, forcing practitioners to curate nearly identical Terraform files across different companies, requiring massive amounts of human capital just to manage the boilerplate.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;why-the-tourist-becomes-a-superhuman&quot;&gt;Why the “Tourist” Becomes a “Superhuman”&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;If AI speaks Terraform like a tourist, it’s because it’s being asked a task that is hard even for human intelligence. However, I’ve noticed a shift: AI speaks BigConfig like a superhuman.&lt;/p&gt;
&lt;p&gt;I’ve had to accept that tools like Claude Code are already operating BigConfig better than I can. The reason is simple: BigConfig provides abstractions similar to React components.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Encapsulation:&lt;/strong&gt; AI doesn’t get lost in the weeds of low-level resource declarations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Composition:&lt;/strong&gt; It can compose high-level abstractions to build complex systems.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Context:&lt;/strong&gt; When the “boring” details are abstracted away, the AI can focus on the architectural intent.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;We are at a crossroads similar to the one web developers faced in 2013. You can either be the developer who insists on manually manipulating the DOM, or you can be the one who learns to build with components.&lt;/p&gt;
&lt;p&gt;The “tourist” phase of AI in DevOps is a temporary byproduct of our own messy, unabstracted architectures. As we move toward higher-level abstractions like BigConfig, the barrier between “intent” and “infrastructure” will vanish. The practitioners who thrive in 2027 won’t be those who can write the most clever HCL from scratch, but those who can design the abstraction that AI is already equipped to manage today.&lt;/p&gt;
&lt;p&gt;Would you like to have a follow-up on this topic? What are your thoughts? I’d love to &lt;a href=&quot;https://www.albertomiorin.com/contact#form&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; hear &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; your experiences.&lt;/p&gt;</content:encoded><category>bigconfig</category><category>devop</category><category>react</category><category>abstraction</category><category>composition</category><category>terraform</category><category>encapsulation</category><category>context</category><category>frontend</category></item><item><title>DevOps Without the Code: Infrastructure as Markdown</title><link>https://www.bigconfig.it/blog/devops-without-the-code-infrastructure-as-markdown/</link><guid isPermaLink="true">https://www.bigconfig.it/blog/devops-without-the-code-infrastructure-as-markdown/</guid><description>Can you build a professional-grade cloud platform without writing a single line of code? By leveraging BigConfig and AI, I created the blueprint of a full-stack infrastructure—complete with DNS, SMTP, and TLS—in just three minutes using nothing but Markdown and declarative data. 

By treating infrastructure as &quot;pure data&quot; rather than complex scripts, BigConfig acts as the &quot;React of DevOps,&quot; encapsulating messy Terraform and Ansible logic into clean, reusable components. This post explores a hands-off workflow where a simple Markdown plan is transformed into a working blueprint, proving that with the right abstractions, non-technical users can manage non-trivial infrastructure as easily as filling in a configuration manifest.

</description><pubDate>Mon, 13 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I wanted to prove a point: you only need Markdown to build non-trivial infrastructure if your abstractions are powerful enough.&lt;/p&gt;
&lt;p&gt;Take the &lt;a href=&quot;https://github.com/amiorin/once&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; once &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; package, for example. It creates a complete platform on any cloud provider—handling DNS, SMTP, and TLS—to deploy your web applications. It’s a self-hosted alternative to Netlify, Vercel, or Fly.io that runs on your own VPS.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;from-idea-to-infrastructure-in-3-minutes&quot;&gt;From Idea to Infrastructure in 3 Minutes&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;I started by manually drafting a simple plan in &lt;code dir=&quot;auto&quot;&gt;user-plan.md&lt;/code&gt;. I then handed that plan to Claude Code and asked it to refine the requirements. Claude generated &lt;code dir=&quot;auto&quot;&gt;final-plan.md&lt;/code&gt;, a detailed technical roadmap.&lt;/p&gt;
&lt;p&gt;Finally, I asked the AI to execute the plan. Within three minutes, I had a working version of the &lt;code dir=&quot;auto&quot;&gt;once&lt;/code&gt; package tailored to my specific requirements.&lt;/p&gt;
&lt;p&gt;The kicker? I didn’t write a single line of Clojure code.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.bigconfig.it/_astro/markdown-flow.C5pGiyUr_Z1hviY5.webp&quot; alt=&quot;flow&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2816&quot; height=&quot;1536&quot;&gt;&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;why-it-works-data-over-code&quot;&gt;Why It Works: Data Over Code&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Even though the underlying engine is Clojure, the configuration is entirely declarative.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code dir=&quot;auto&quot;&gt;bb.edn&lt;/code&gt; file feels more like a Makefile than a programming script.&lt;/li&gt;
&lt;li&gt;The EDN format is essentially JSON with a few more superpowers.&lt;/li&gt;
&lt;li&gt;The logic is hidden; what’s left is just a list of parameters to tailor the behavior.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I can easily imagine a non-technical user asking an AI to “fill in the missing values” and run the tasks until they work.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;bigconfig-as-the-react-of-devops&quot;&gt;BigConfig as the “React of DevOps”&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;How is this possible? &lt;strong&gt;BigConfig is the React of Agentic DevOps&lt;/strong&gt;. Just as React allows you to build complex frontends using high-level components, BigConfig encapsulates messy Terraform and Ansible code into clean, reusable “infrastructure components.” It’s like HCL (Terraform) modules on steroids because it supports any tool—Ansible, Terraform, Helm, and more—under one unified, data-driven interface.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-milestones&quot;&gt;The milestones&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;You don’t have to take my word for it. Below are the exact inputs I used so you can reproduce the experiment yourself.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;1-the-human-draft-user-planmd&quot;&gt;1. The Human Draft (&lt;code dir=&quot;auto&quot;&gt;user-plan.md&lt;/code&gt;)&lt;/h3&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;user-plan.md&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;You need to create a project to deploy a web application.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;This project contains only three files: a &lt;/span&gt;&lt;span&gt;`bb.edn`&lt;/span&gt;&lt;span&gt; file, a &lt;/span&gt;&lt;span&gt;`deps.edn`&lt;/span&gt;&lt;span&gt; file, and a &lt;/span&gt;&lt;span&gt;`.envrc`&lt;/span&gt;&lt;span&gt; file&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;The deps.edn will contain the dependency https://github.com/amiorin/once&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;You can download https://github.com/amiorin/once to see how it works.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;https://github.com/amiorin/once is built on top of https://github.com/amiorin/big-config&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;You can download https://github.com/amiorin/big-config to see how it works.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;The bb.edn contains a dependency :local/root to the current directory containing the file deps.edn that contains a dependency to https://github.com/amiorin/once&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;The domain is bigconfig.online.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;The host is www.bigconfig.online.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;The container is ghcr.io/amiorin/big-config-website:latest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;The compute provider is OCI with 6 GB, 1 core, and 50 GB of disk.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;The dns provider is Cloudflare.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;The smtp provider is Resend.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;The backend provider is S3.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;You need to need to create the &lt;/span&gt;&lt;span&gt;`.envrc`&lt;/span&gt;&lt;span&gt; environment variable for the credentials, I will provide the values later.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;2-the-ai-refined-plan-final-planmd&quot;&gt;2. The AI-Refined Plan (&lt;code dir=&quot;auto&quot;&gt;final-plan.md&lt;/code&gt;)&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Claude expanded my draft into a robust plan:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;final-plan.md&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Deployment plan: bigconfig.online&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;A three-file project that uses &lt;/span&gt;&lt;span&gt;`amiorin/once`&lt;/span&gt;&lt;span&gt; to provision OCI + Cloudflare DNS + Resend SMTP + an S3 tofu backend, and hands off container lifecycle + TLS to &lt;/span&gt;&lt;span&gt;`basecamp/once`&lt;/span&gt;&lt;span&gt; running on the VM.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;`amiorin/once`&lt;/span&gt;&lt;span&gt; is built on top of &lt;/span&gt;&lt;span&gt;`big-config`&lt;/span&gt;&lt;span&gt;. Before wiring anything, clone both repos locally and confirm the assumptions in the &quot;Open questions&quot; section below — several env-var names and behaviors here are placeholders until verified against the source.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; https://github.com/amiorin/once&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; https://github.com/amiorin/big-config&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; https://github.com/basecamp/once (installed on the VM; runs the container + handles TLS)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;## Files&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;deps.edn&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt; — depends on &lt;/span&gt;&lt;span&gt;`io.github.amiorin/once`&lt;/span&gt;&lt;span&gt; via &lt;/span&gt;&lt;span&gt;`:git/sha`&lt;/span&gt;&lt;span&gt;. No &lt;/span&gt;&lt;span&gt;`:paths`&lt;/span&gt;&lt;span&gt;, no override namespaces.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;bb.edn&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt; — &lt;/span&gt;&lt;span&gt;`:deps {:local/root &quot;.&quot;}`&lt;/span&gt;&lt;span&gt; to pull the sibling &lt;/span&gt;&lt;span&gt;`deps.edn`&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;`:tasks`&lt;/span&gt;&lt;span&gt; that &lt;/span&gt;&lt;span&gt;`:require`&lt;/span&gt;&lt;span&gt; once&apos;s tasks so &lt;/span&gt;&lt;span&gt;`bb once create`&lt;/span&gt;&lt;span&gt; / &lt;/span&gt;&lt;span&gt;`bb once delete`&lt;/span&gt;&lt;span&gt; are exposed, and a top-level Clojure data structure holding the project options (profile, host, container image, &quot;don&apos;t create APEX&quot;, etc.) that once&apos;s tasks consume.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;.envrc&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt; — exports the variables listed below. Follow once&apos;s convention of sourcing &lt;/span&gt;&lt;span&gt;`.envrc.private`&lt;/span&gt;&lt;span&gt; for secrets, and add &lt;/span&gt;&lt;span&gt;`.envrc.private`&lt;/span&gt;&lt;span&gt; to &lt;/span&gt;&lt;span&gt;`.gitignore`&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;## Targets&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;Domain&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;`bigconfig.online`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;Host&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;`www.bigconfig.online`&lt;/span&gt;&lt;span&gt; — &lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;do not create an APEX record&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;; if once&apos;s &lt;/span&gt;&lt;span&gt;`website`&lt;/span&gt;&lt;span&gt; profile creates one by default, override it from the &lt;/span&gt;&lt;span&gt;`bb.edn`&lt;/span&gt;&lt;span&gt; options&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;Container&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;`ghcr.io/amiorin/big-config-website:latest`&lt;/span&gt;&lt;span&gt; — must publish a &lt;/span&gt;&lt;span&gt;`linux/arm64`&lt;/span&gt;&lt;span&gt; variant, since the OCI free tier is Ampere A1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;Compute&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;: OCI &lt;/span&gt;&lt;span&gt;`VM.Standard.A1.Flex`&lt;/span&gt;&lt;span&gt;, 1 OCPU, 6 GB RAM, 50 GB boot volume, Ubuntu LTS&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;DNS&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;: Cloudflare&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;SMTP (domain provisioning + outbound mail)&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;: Resend&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;Tofu backend&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;: S3 — the state bucket must exist before the first &lt;/span&gt;&lt;span&gt;`tofu init`&lt;/span&gt;&lt;span&gt;; pre-create it manually or add a bootstrap task in &lt;/span&gt;&lt;span&gt;`bb.edn`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;## Container lifecycle&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;`amiorin/once`&lt;/span&gt;&lt;span&gt; provisions the VM and (via Ansible) installs &lt;/span&gt;&lt;span&gt;`basecamp/once`&lt;/span&gt;&lt;span&gt; on it. &lt;/span&gt;&lt;span&gt;`basecamp/once`&lt;/span&gt;&lt;span&gt; then pulls and runs the container and handles TLS via Let&apos;s Encrypt. As a result, &lt;/span&gt;&lt;span&gt;`amiorin/once`&lt;/span&gt;&lt;span&gt; does &lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;not&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt; need a &lt;/span&gt;&lt;span&gt;`docker run`&lt;/span&gt;&lt;span&gt; role, a systemd unit, or a compose file of its own — but &lt;/span&gt;&lt;span&gt;`basecamp/once`&lt;/span&gt;&lt;span&gt; has its own configuration surface that must be wired through.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;## Environment variables (&lt;/span&gt;&lt;span&gt;`.envrc`&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Values to be supplied later. &lt;/span&gt;&lt;span&gt;`BC_PAR_*`&lt;/span&gt;&lt;span&gt; are once/big-config parameter overrides; exact spellings must be confirmed against &lt;/span&gt;&lt;span&gt;`src/`&lt;/span&gt;&lt;span&gt; in &lt;/span&gt;&lt;span&gt;`amiorin/once`&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;Domain &amp;#x26; tofu backend&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`BC_PAR_DOMAIN=bigconfig.online`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`BC_PAR_HOST=www.bigconfig.online`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`BC_PAR_PROVIDER_BACKEND=s3`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`BC_PAR_S3_BUCKET`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`BC_PAR_S3_REGION`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`AWS_ACCESS_KEY_ID`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`AWS_SECRET_ACCESS_KEY`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;OCI (compute)&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt; — once&apos;s OCI module will read some combination of these; confirm exact names:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; tenancy OCID&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; user OCID&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; API key fingerprint&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; API private key path&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; region&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; compartment OCID&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; availability domain&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; SSH public key (authorized on the VM)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; SSH private key path (used by Ansible to log in)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;Cloudflare (DNS)&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`BC_PAR_CLOUDFLARE_API_TOKEN`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`BC_PAR_CLOUDFLARE_ZONE_ID`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;Resend&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`BC_PAR_RESEND_API_KEY`&lt;/span&gt;&lt;span&gt; — provisioning SPF/DKIM records on the domain&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`BC_PAR_RESEND_PASSWORD`&lt;/span&gt;&lt;span&gt; — SMTP auth, passed into the container so the website can send mail&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;basecamp/once (on the VM)&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`BC_PAR_ONCE_LETSENCRYPT_EMAIL`&lt;/span&gt;&lt;span&gt; — Let&apos;s Encrypt registration address&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`BC_PAR_ONCE_LICENSE_KEY`&lt;/span&gt;&lt;span&gt; — only if &lt;/span&gt;&lt;span&gt;`big-config-website`&lt;/span&gt;&lt;span&gt; is deployed as a licensed Basecamp product; omit if it&apos;s a bring-your-own container&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;Container registry (ghcr.io)&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt; — only if the image is private:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`GHCR_USERNAME`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`GHCR_TOKEN`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;## Guideline&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Avoid writing Clojure source files. Everything is data: project options live in &lt;/span&gt;&lt;span&gt;`bb.edn`&lt;/span&gt;&lt;span&gt; as a Clojure map, secrets live in &lt;/span&gt;&lt;span&gt;`.envrc`&lt;/span&gt;&lt;span&gt; / &lt;/span&gt;&lt;span&gt;`.envrc.private`&lt;/span&gt;&lt;span&gt;. The only &quot;code&quot; is the &lt;/span&gt;&lt;span&gt;`:tasks`&lt;/span&gt;&lt;span&gt; wiring in &lt;/span&gt;&lt;span&gt;`bb.edn`&lt;/span&gt;&lt;span&gt; that hands the options map to once&apos;s tasks.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;## Open questions to resolve before implementing&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;1.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;Options loading.&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt; Does &lt;/span&gt;&lt;span&gt;`amiorin/once`&lt;/span&gt;&lt;span&gt; accept project options as a data arg passed from &lt;/span&gt;&lt;span&gt;`bb.edn`&lt;/span&gt;&lt;span&gt;, or does it require a Clojure namespace on the classpath? Determines whether the &quot;no source files&quot; guideline is achievable as-is.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;2.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;Profile.&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt; Does once&apos;s &lt;/span&gt;&lt;span&gt;`website`&lt;/span&gt;&lt;span&gt; profile already target &lt;/span&gt;&lt;span&gt;`bigconfig.online`&lt;/span&gt;&lt;span&gt; / &lt;/span&gt;&lt;span&gt;`www`&lt;/span&gt;&lt;span&gt; / the ghcr image? If not, how is it overridden from the &lt;/span&gt;&lt;span&gt;`bb.edn`&lt;/span&gt;&lt;span&gt; options map?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;3.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;Exact &lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;BC_PAR_*&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt; names&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt; for OCI, SSH keys, container image, and basecamp/once inputs — derive from once&apos;s source rather than guessing.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;4.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;basecamp/once scope.&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt; Does it host arbitrary containers, or does it require a product manifest? What does it actually need at install time (email, license, domain)?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;5.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;Image.&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt; Is &lt;/span&gt;&lt;span&gt;`ghcr.io/amiorin/big-config-website:latest`&lt;/span&gt;&lt;span&gt; public, and does it publish &lt;/span&gt;&lt;span&gt;`linux/arm64`&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;6.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;APEX.&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt; Does the &lt;/span&gt;&lt;span&gt;`website`&lt;/span&gt;&lt;span&gt; profile create an apex record by default? If yes, wire the override through the &lt;/span&gt;&lt;span&gt;`bb.edn`&lt;/span&gt;&lt;span&gt; options map.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;7.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;S3 bucket bootstrap.&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt; Pre-create manually, or automate via a bootstrap task in &lt;/span&gt;&lt;span&gt;`bb.edn`&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;3-the-result-bbedn&quot;&gt;3. The Result: &lt;code dir=&quot;auto&quot;&gt;bb.edn&lt;/code&gt;&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;This is the heart of the deployment: a concise mix of requirements, data structures, and tasks. Notice how it reads like a declarative configuration manifest rather than a complex program.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;bb.edn&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;:deps&lt;/span&gt;&lt;span&gt; {bigconfig/online {&lt;/span&gt;&lt;span&gt;:local/root&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;}}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:tasks&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;:requires&lt;/span&gt;&lt;span&gt; ([big-config.render &lt;/span&gt;&lt;span&gt;:as&lt;/span&gt;&lt;span&gt; render]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;             &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[big-config.workflow &lt;/span&gt;&lt;span&gt;:as&lt;/span&gt;&lt;span&gt; workflow]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;             &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[io.github.amiorin.once.package &lt;/span&gt;&lt;span&gt;:as&lt;/span&gt;&lt;span&gt; package]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;             &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[io.github.amiorin.once.params &lt;/span&gt;&lt;span&gt;:as&lt;/span&gt;&lt;span&gt; params]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;             &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[io.github.amiorin.once.tools &lt;/span&gt;&lt;span&gt;:as&lt;/span&gt;&lt;span&gt; tools])&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;:init&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; bigconfig-online&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;:big-config.render/profile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;online&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;           &lt;/span&gt;&lt;span&gt;:big-config.workflow/params&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;           &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;:package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;online&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;:domain&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;bigconfig.online&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;:provider-compute&lt;/span&gt;&lt;span&gt;           &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;oci&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;:oci-config-file-profile&lt;/span&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;DEFAULT&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;:oci-compartment-id&lt;/span&gt;&lt;span&gt;         &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;FILL-ME-IN&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;:oci-subnet-id&lt;/span&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;FILL-ME-IN&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;:oci-availability-domain&lt;/span&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;FILL-ME-IN&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;:oci-display-name&lt;/span&gt;&lt;span&gt;           &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;bigconfig-online&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;:oci-shape&lt;/span&gt;&lt;span&gt;                  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;VM.Standard.A1.Flex&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;:oci-ocpus&lt;/span&gt;&lt;span&gt;                  &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;:oci-memory-in-gbs&lt;/span&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;6&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;:oci-boot-volume-size-in-gbs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;50&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;:oci-boot-volume-vpus-per-gb&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;30&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;:oci-ssh-authorized-keys&lt;/span&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;~/.ssh/id_ed25519.pub&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;:provider-smtp&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;resend&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;:resend-server&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;smtp.resend.com&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;:resend-port&lt;/span&gt;&lt;span&gt;     &lt;/span&gt;&lt;span&gt;587&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;:resend-username&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;resend&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;:provider-dns&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;cloudflare&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;:provider-backend&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;s3&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;:s3-bucket&lt;/span&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;FILL-ME-IN&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;:s3-region&lt;/span&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;eu-west-1&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;:once&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt;:applications&lt;/span&gt;&lt;span&gt; [{&lt;/span&gt;&lt;span&gt;:host&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;www.bigconfig.online&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                   &lt;/span&gt;&lt;span&gt;:image&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;ghcr.io/amiorin/big-config-website&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;}]}}})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;once           {&lt;/span&gt;&lt;span&gt;:doc&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;bb once create | bb once delete&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                  &lt;/span&gt;&lt;span&gt;:task&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;package/once*&lt;/span&gt;&lt;span&gt; *command-line-args* bigconfig-online)}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tofu           {&lt;/span&gt;&lt;span&gt;:doc&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;bb tofu render tofu:init tofu:apply:-auto-approve&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                  &lt;/span&gt;&lt;span&gt;:task&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;tools/tofu*&lt;/span&gt;&lt;span&gt; *command-line-args* (&lt;/span&gt;&lt;span&gt;params/once-opts&lt;/span&gt;&lt;span&gt; bigconfig-online))}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tofu-smtp      {&lt;/span&gt;&lt;span&gt;:doc&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;bb tofu-smtp render tofu:init tofu:apply:-auto-approve&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                  &lt;/span&gt;&lt;span&gt;:task&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;tools/tofu-smtp*&lt;/span&gt;&lt;span&gt; *command-line-args* (&lt;/span&gt;&lt;span&gt;params/once-opts&lt;/span&gt;&lt;span&gt; bigconfig-online))}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tofu-dns       {&lt;/span&gt;&lt;span&gt;:doc&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;bb tofu-dns render tofu:init tofu:apply:-auto-approve&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                  &lt;/span&gt;&lt;span&gt;:task&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;tools/tofu-dns*&lt;/span&gt;&lt;span&gt; *command-line-args* (&lt;/span&gt;&lt;span&gt;params/once-opts&lt;/span&gt;&lt;span&gt; bigconfig-online))}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tofu-smtp-post {&lt;/span&gt;&lt;span&gt;:doc&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;bb tofu-smtp-post render tofu:init tofu:apply:-auto-approve&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                  &lt;/span&gt;&lt;span&gt;:task&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;tools/tofu-smtp-post*&lt;/span&gt;&lt;span&gt; *command-line-args* (&lt;/span&gt;&lt;span&gt;params/once-opts&lt;/span&gt;&lt;span&gt; bigconfig-online))}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ansible        {&lt;/span&gt;&lt;span&gt;:doc&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;bb ansible render -- ansible-playbook main.yml&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                  &lt;/span&gt;&lt;span&gt;:task&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;tools/ansible*&lt;/span&gt;&lt;span&gt; *command-line-args* (&lt;/span&gt;&lt;span&gt;params/once-opts&lt;/span&gt;&lt;span&gt; bigconfig-online))}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ansible-local  {&lt;/span&gt;&lt;span&gt;:doc&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;bb ansible-local render -- ansible-playbook main.yml&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                  &lt;/span&gt;&lt;span&gt;:task&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;tools/ansible-local*&lt;/span&gt;&lt;span&gt; *command-line-args* (&lt;/span&gt;&lt;span&gt;params/once-opts&lt;/span&gt;&lt;span&gt; bigconfig-online))}}}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;By treating infrastructure as pure data, we remove the “syntax tax” of Clojure. Whether you are a seasoned developer or a non-technical founder, BigConfig provides a path to professional-grade DevOps without the steep learning curve.&lt;/p&gt;
&lt;p&gt;Would you like to have a follow-up on this topic? What are your thoughts? I’d love to &lt;a href=&quot;https://www.albertomiorin.com/contact#form&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; hear &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; your experiences.&lt;/p&gt;</content:encoded><category>package manager</category><category>bigconfig</category><category>once</category><category>vibe coding</category><category>vibe operations</category><category>react</category><category>devops</category><category>claude code</category><category>claude</category><category>hetzner</category><category>oci</category><category>oracle</category><category>resend</category><category>astro</category><category>markdown</category><category>abstractions</category></item><item><title>BigConfig: The &quot;React&quot; for Agentic DevOps</title><link>https://www.bigconfig.it/blog/bigconfig-the-react-for-agentic-devops/</link><guid isPermaLink="true">https://www.bigconfig.it/blog/bigconfig-the-react-for-agentic-devops/</guid><description>Stop asking AI to write &quot;assembly code&quot; for your deployment. Writing 500-line Terraform files is brittle, verbose, and a recipe for hallucination. In this article, we explore a paradigm shift: Component-Based DevOps. 

By using BigConfig and the &quot;once&quot; package, you can move away from manual scripting and toward a &quot;React-style&quot; architecture for infrastructure. Learn how to combine Claude Code with high-level abstractions to build your own personal PaaS on providers like Hetzner and OCI—achieving deterministic, zero-touch deployments without the DSL headache. It&apos;s time to stop writing infrastructure and start composing it.

</description><pubDate>Sun, 12 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When you ask an AI to write a 500-line Terraform file, you are essentially asking it to write assembly code for your deployment. It’s brittle, verbose, and prone to “hallucinated” parameters.&lt;/p&gt;
&lt;p&gt;The shift is simple: stop using DevOps tools like Terraform and Ansible directly. Instead, ask your Agent to use React-style components for DevOps. By shifting the Agent’s focus from “writing code” to “composing components,” you gain the same benefits developers got when they moved from jQuery to React: reusability, predictability, and a massive reduction in logic errors.&lt;/p&gt;
&lt;p&gt;Enter BigConfig: the “React for DevOps.” It allows you to use high-level abstractions that ensure your Agent’s output is deterministic, modular, and cost-effective.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-vision-agentic--data-driven-devops&quot;&gt;The Vision: Agentic + Data-Driven DevOps&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;By combining Claude Code (Max Effort with planning) with BigConfig and the &lt;a href=&quot;https://github.com/amiorin/once&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; once &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; package, you create a platform-as-a-service (PaaS) experience—like Vercel or Netlify—but running on your own hardware. You don’t need to master Clojure or complex DSLs; you just provide the intent, and the Agent handles the implementation using reliable components.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;the-root-directory&quot;&gt;The root directory&lt;/h3&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Your App source code:&lt;/strong&gt; An Astro managed via &lt;code dir=&quot;auto&quot;&gt;pnpm&lt;/code&gt;. But Claude can adapt it to Rails/Django/Next/Phoenix/Spring.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/amiorin/big-config&quot;&gt;BigConfig&lt;/a&gt; source code:&lt;/strong&gt; The framework for “infrastructure components.” It reduces token consumption by using high-level abstractions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/amiorin/once&quot;&gt;Once&lt;/a&gt; source code:&lt;/strong&gt; A pre-built component implemented in BigConfig that turns any VPS into a personal PaaS.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Glue files:&lt;/strong&gt; GitHub Actions and Docker to bridge your code to your cloud.&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;h2 id=&quot;the-workflow-zero-touch-deployment&quot;&gt;The Workflow: Zero-Touch Deployment&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;I use Claude Code (4.6 Max Effort with planning) to manage the entire lifecycle. I create a directory containing everything and then I simply ask Claude to deploy.&lt;/p&gt;
&lt;p&gt;Out of the box, this setup supports &lt;strong&gt;OCI, DigitalOcean, and Hetzner&lt;/strong&gt; for compute, &lt;strong&gt;Resend&lt;/strong&gt; for email, and &lt;strong&gt;Cloudflare&lt;/strong&gt; for DNS. If you need a different provider, you don’t write the code; you just ask Claude to implement the &lt;code dir=&quot;auto&quot;&gt;main.tf&lt;/code&gt; for you.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;1-the-web-server-caddyfile&quot;&gt;1. The Web Server (Caddyfile)&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;We use Caddy for serving the Astro SSG website. This handles our health checks too.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Caddyfile&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;:80 {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;handle /up {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;header Content-Type text/plain; charset=utf-8&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;respond `OK` 200&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;handle {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;root * /srv&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;file_server&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;2-the-container-dockerfile&quot;&gt;2. The Container (Dockerfile)&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;A multi-stage build that keeps our final image slim and secure. Note the inclusion of &lt;a href=&quot;https://d2lang.com/&quot;&gt;D2&lt;/a&gt; for documentation as an example of a build of Astro with a plugin.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Dockerfile&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; node:22-alpine &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; builder&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;RUN&lt;/span&gt;&lt;span&gt; apk add --no-cache curl make &amp;#x26;&amp;#x26; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;curl -fsSL https://d2lang.com/install.sh | sh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;RUN&lt;/span&gt;&lt;span&gt; corepack enable &amp;#x26;&amp;#x26; corepack prepare pnpm@latest --activate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;WORKDIR&lt;/span&gt;&lt;span&gt; /app&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;COPY&lt;/span&gt;&lt;span&gt; package.json pnpm-lock.yaml* ./&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;RUN&lt;/span&gt;&lt;span&gt; pnpm install --frozen-lockfile&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;COPY&lt;/span&gt;&lt;span&gt; . .&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;RUN&lt;/span&gt;&lt;span&gt; pnpm build&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; caddy:2-alpine&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;COPY&lt;/span&gt;&lt;span&gt; Caddyfile /etc/caddy/Caddyfile&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;COPY&lt;/span&gt;&lt;span&gt; --from=builder /app/dist /srv&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;EXPOSE&lt;/span&gt;&lt;span&gt; 80&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;3-the-pipeline-github-action&quot;&gt;3. The Pipeline (GitHub Action)&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;This action builds the image for &lt;strong&gt;ARM architecture&lt;/strong&gt; (optimized for modern VPS providers) and pushes it to the GitHub Container Registry (GHCR). Remember to make the image public.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;.github/workflows/docker-publish.yml&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Build and Publish Docker Image&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;on&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;branches&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;main&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;# Adjusted to standard branch naming&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;workflow_dispatch&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;REGISTRY&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;ghcr.io&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;IMAGE_NAME&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ github.repository }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;jobs&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;build-and-push&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;runs-on&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;ubuntu-24.04-arm&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;permissions&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;contents&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;read&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;packages&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;steps&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Checkout repository&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;uses&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;actions/checkout@v4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Log in to GHCR&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;uses&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;docker/login-action@v3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;with&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;registry&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ env.REGISTRY }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;username&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ github.actor }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;password&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Extract Docker metadata&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;uses&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;docker/metadata-action@v5&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;with&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;images&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type=raw,value=latest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type=sha&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Set up Docker Buildx&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;uses&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;docker/setup-buildx-action@v3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Build and push&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;uses&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;docker/build-push-action@v6&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;with&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ steps.meta.outputs.tags }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;labels&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ steps.meta.outputs.labels }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;cache-from&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;type=gha&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;cache-to&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;type=gha,mode=max&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;To deploy, you simply point Claude to your root folder and say:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“Adapt my Astro project to create a GHCR docker image and update the Once project to use this container on a Hetzner VPS.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Because of BigConfig, Claude isn’t guessing how to configure a server—it’s just filling in the props for a “Component” that already knows how to work. This is the future of DevOps: high level abstraction and Agents.&lt;/p&gt;
&lt;p&gt;Would you like to have a follow-up on this topic? What are your thoughts? I’d love to &lt;a href=&quot;https://www.albertomiorin.com/contact#form&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; hear &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; your experiences.&lt;/p&gt;</content:encoded><category>package manager</category><category>bigconfig</category><category>once</category><category>vibe coding</category><category>vibe operations</category><category>react</category><category>jquery</category><category>devops</category><category>claude code</category><category>claude</category><category>hetzner</category><category>oci</category><category>oracle</category><category>resend</category><category>astro</category></item><item><title>The Evolution of DevOps: From Separation by Technology to Separation by Concerns</title><link>https://www.bigconfig.it/blog/the-evolution-of-devops-from-separation-by-technology-to-separation-by-concerns/</link><guid isPermaLink="true">https://www.bigconfig.it/blog/the-evolution-of-devops-from-separation-by-technology-to-separation-by-concerns/</guid><description>Drawing a parallel to the React revolution in frontend development, this article explores how shifting from Separation by Technology to Separation by Concerns can solve the modern infrastructure bottleneck. Learn how BigConfig uses a component-based approach to unify Kubernetes, Terraform, and Ansible, enabling modularity, interchangeability, and a &quot;clean interface&quot; for your entire stack.

</description><pubDate>Thu, 02 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The current state of DevOps feels eerily similar to the dark ages of Frontend development before the &lt;a href=&quot;https://react.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; React &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; revolution.&lt;/p&gt;
&lt;p&gt;In the early days of the web, we practiced Separation by Technology. We kept our CSS in one folder, our HTML in another, and our JavaScript in a third. We were told this was clean, but in reality, it was a nightmare to maintain. To change a single button, you had to hunt through three different files in three different directories.&lt;/p&gt;
&lt;p&gt;React changed the game by introducing Separation by Concerns. It recognized that a Component is the natural unit of work. A component encapsulates its logic, structure, and styling into a single, cohesive module.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-modern-devops-bottleneck&quot;&gt;The Modern DevOps Bottleneck&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Today, DevOps is still stuck in the Separation by Technology phase. We segregate our logic by the tools we use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Terraform/OpenTofu&lt;/strong&gt; for infrastructure.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ansible&lt;/strong&gt; for configuration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kubernetes&lt;/strong&gt; for orchestration.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While Kubernetes has moved us toward immutability, it has also introduced the &lt;a href=&quot;https://www.bigconfig.it/blog/the-yaml-trap-escaping-greenspun-s-tenth-rule-with-bigconfig/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; YAML trap &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;. Furthermore, the real world isn’t always immutable. We don’t discard a developer’s machine every time we need a minor package upgrade, and stateful services (like databases) require delicate, mutable handling that pure-container strategies struggle to manage.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;enter-bigconfig-the-component-based-approach&quot;&gt;Enter BigConfig: The Component-Based Approach&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;BigConfig applies the React philosophy to DevOps. Instead of managing a Terraform repo and an Ansible repo you manage Packages.&lt;/p&gt;
&lt;p&gt;For example, the BigConfig Package &lt;a href=&quot;https://github.com/amiorin/once&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; once &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; utilize four Terraform projects and two Ansible projects under the hood, but to the user, it is a single, functional unit.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;the-coupling-problem-a-practical-example&quot;&gt;The Coupling Problem: A Practical Example&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Consider a simple task: checking if the the server can send emails. Ansible needs to create a &lt;code dir=&quot;auto&quot;&gt;.mailrc&lt;/code&gt; file so the operator can use &lt;code dir=&quot;auto&quot;&gt;s-nail&lt;/code&gt; CLI to test the SMTP connection during an incident.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The Traditional Way:&lt;/strong&gt; The infrastructure (SMTP infrastructure) is defined in OpenTofu, but the &lt;code dir=&quot;auto&quot;&gt;.mailrc&lt;/code&gt; configuration lives in an Ansible folder. This creates strong coupling. If you change your SMTP provider or port, you must manually update two different repositories.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The BigConfig Way:&lt;/strong&gt; Responsibility is delegated, not segregated. The Tofu SMTP component is responsible for generating the configuration data, while Ansible is simply the delivery mechanism that places the file on the server. The file source path acts as a clean interface. One change in the SMTP component automatically flows through the system.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;modularity-and-referential-transparency&quot;&gt;Modularity and Referential Transparency&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The power of a Separation of Concerns architecture lies in its modularity. In the BigConfig Package once, we categorize providers into functional types: Compute, DNS, and SMTP.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Interchangeability:&lt;/strong&gt; Our Compute component has three implementations: OCI, Hetzner, and DigitalOcean. Because these are built as components, you can swap one for another without touching your DNS or SMTP logic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Referential Transparency:&lt;/strong&gt; We utilize “no-infra” components as placeholders. This allows you to bring your own pre-existing infrastructure into the system. You can replace a live resource with its values without changing the behavior of the rest of the stack. In programming, this is called referential transparency; in DevOps, it makes brownfields easier to support.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No Degradation of Experience:&lt;/strong&gt; Usually, abstractions make debugging harder. BigConfig is designed so that the composition of components doesn’t hide the underlying tools. You can manage the entire Composition as one, or drop down to debug a single component (like a specific Tofu component) without any additional configuration overhead.&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;We are moving past the era of “Tool-First” DevOps. By adopting a component-based mindset, we can stop managing scripts and start building systems that are as modular, testable, and maintainable as our modern frontend applications.&lt;/p&gt;
&lt;p&gt;Would you like to have a follow-up on this topic? What are your thoughts? I’d love to &lt;a href=&quot;https://www.albertomiorin.com/contact#form&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; hear &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; your experiences.&lt;/p&gt;</content:encoded><category>package manager</category><category>bigconfig</category><category>once</category><category>separation by concerns</category><category>separation by technology</category><category>ansible</category><category>terraform</category><category>kubernetes</category><category>referential transparency</category><category>component</category></item><item><title>Vibe Coding Meets Vibe Ops: Automating the Last Mile of Deployment</title><link>https://www.bigconfig.it/blog/vibe-coding-meets-vibe-ops-automating-the-last-mile-of-deployment/</link><guid isPermaLink="true">https://www.bigconfig.it/blog/vibe-coding-meets-vibe-ops-automating-the-last-mile-of-deployment/</guid><description>Vibe coding has revolutionized how we build apps, but deployment is still stuck in the manual ages. Discover how BigConfig&apos;s once bridges the gap, automating compute, DNS, and SMTP into a seamless pipeline that brings us one step closer to Natural Language Infrastructure.

</description><pubDate>Wed, 01 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Vibe coding has made building web applications faster and more intuitive than ever, but the Ops side of the house hasn’t quite kept up. While 37signals’ &lt;a href=&quot;https://github.com/basecamp/once&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; ONCE &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; simplified the deployment architecture for vibe coders, managing the underlying infrastructure—Compute, DNS, and SMTP—remains a fragmented, manual process.&lt;/p&gt;
&lt;p&gt;Enter the BigConfig &lt;a href=&quot;https://github.com/amiorin/once&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; once &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;: the missing link that automates the manual out of your deployment pipeline.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;zero-friction-infrastructure&quot;&gt;Zero-Friction Infrastructure&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;BigConfig turns complex infrastructure provisioning into a few simple terminal commands. No more hunting for IP addresses or manually editing SSH configs.&lt;/p&gt;
&lt;ol role=&quot;list&quot;&gt;
&lt;li&gt;Provision your VPS instantly
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;clone&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://github.com/amiorin/once&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;once&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;bb&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;once&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;Jump straight into your server (SSH config is auto-populated)
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ssh&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;once&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;Start ONCE TUI in your server to deploy your vibe coded application
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;once&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://www.bigconfig.it/_astro/once.CY_vO7LE_24MOfa.webp&quot; alt=&quot;once&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2232&quot; height=&quot;1466&quot;&gt;
&lt;img src=&quot;https://www.bigconfig.it/_astro/apps.CDZctbMl_t0li1.webp&quot; alt=&quot;apps&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;4480&quot; height=&quot;2520&quot;&gt;&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;1-compute&quot;&gt;1. Compute&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;While &lt;a href=&quot;https://www.oracle.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Oracle &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; and &lt;a href=&quot;https://www.hetzner.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Hetzner &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; are supported out of the box, BigConfig is built for extensibility. Adding a new compute provider is trivial, ensuring you aren’t locked into a single ecosystem.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;2-dns&quot;&gt;2. DNS&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://www.cloudflare.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Cloudflare &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; integration is ready to go. Point your domain and let BigConfig handle the records, ensuring your application is reachable as soon as the compute instance is live.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;3-smtp&quot;&gt;3. SMTP&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Transactional email is handled via &lt;a href=&quot;https://resend.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Resend &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; by default. Like the other modules, swapping this for another SMTP service is simple and straightforward.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-vision-dev--ops-in-plain-english&quot;&gt;The Vision: Dev &amp;#x26; Ops in Plain English&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The roadmap for BigConfig is headed toward a Natural Language Infrastructure. The next milestone is teaching Claude Code to utilize the BigConfig &lt;a href=&quot;https://github.com/amiorin/once&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; once &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Soon, a vibe coder won’t just write code in English—they will manage their entire production environment through conversation, blurring the line between development and operations entirely.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;current-manual-hurdles&quot;&gt;Current Manual Hurdles&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;We’ve automated the heavy lifting, but a few Human-in-the-Loop steps remain—mostly due to provider security and billing requirements. In the age of agentification, these hurdles may not last long:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Domain Registration:&lt;/strong&gt; Snagging budget domains (like &lt;code dir=&quot;auto&quot;&gt;.website&lt;/code&gt; or &lt;code dir=&quot;auto&quot;&gt;.online&lt;/code&gt; for $0.85) via &lt;a href=&quot;https://www.namecheap.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Namecheap &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; is still a manual checkout process.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Oracle Cloud Setup:&lt;/strong&gt; Creating an account requires a physical credit card. To unlock the Always Free tier (4 cores, 24 GB RAM), you must manually switch to a Pay-As-You-Go account and generate your API credentials.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Namecheap-Cloudflare Bridge:&lt;/strong&gt; Because Namecheap doesn’t offer an API for smaller accounts, pointing your Namecheap domain to Cloudflare DNS and generating an API token remains a manual task.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resend Setup:&lt;/strong&gt; Account creation and initial API key generation must be done via their web dashboard.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The gap between vibe coding a masterpiece and actually shipping it to a live URL is narrowing. By wrapping the fragmented worlds of VPS provisioning, DNS routing, and SMTP configuration into a unified, programmable workflow, BigConfig transforms deployment from a weekend chore into a minor detail. While a few manual gates like billing and domain registration remain—reminders of the physical world we still inhabit—the path toward a fully conversational, Natural Language Infrastructure is clear.&lt;/p&gt;
&lt;p&gt;We are rapidly approaching a reality where the Ops in DevOps is as effortless as the Dev. If you’re ready to stop wrestling with dashboards and start shipping at the speed of thought, it’s time to give the BigConfig a spin.&lt;/p&gt;
&lt;p&gt;Would you like to have a follow-up on this topic? What are your thoughts? I’d love to &lt;a href=&quot;https://www.albertomiorin.com/contact#form&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; hear &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; your experiences.&lt;/p&gt;</content:encoded><category>package manager</category><category>bigconfig</category><category>once</category><category>vibe coding</category><category>vibe operations</category><category>hetzner</category><category>oci</category><category>oracle</category><category>resend</category><category>namecheap</category></item><item><title>The Power of Framing: Why BigConfig is Rebranding as a Package Manager</title><link>https://www.bigconfig.it/blog/the-power-of-framing-why-bigconfig-is-rebranding-as-a-package-manager/</link><guid isPermaLink="true">https://www.bigconfig.it/blog/the-power-of-framing-why-bigconfig-is-rebranding-as-a-package-manager/</guid><description>BigConfig started as a modest script to simplify complex Terraform projects, but it has grown into something far more ambitious. By moving beyond the &quot;library&quot; mindset and embracing the role of a package manager for infrastructure, BigConfig overcomes the traditional barriers of language adoption. This article explores how a unified Clojure-based workflow—spanning the REPL, the Shell, and the Library—offers a total solution for developers tired of juggling fragmented YAML templates and rigid deployment schemas.

</description><pubDate>Fri, 13 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;BigConfig began as a simple &lt;a href=&quot;https://babashka.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Babashka &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; script designed to DRY up a complex &lt;a href=&quot;https://developer.hashicorp.com/terraform&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Terraform &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; project for a data platform. Since those humble beginnings, it has evolved through several iterations into a robust template and workflow engine. But as the tool matured, I realized that technical power wasn’t enough; the way it was framed was the true barrier to adoption.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-language-barrier-and-the-loophole&quot;&gt;The Language Barrier (and the Loophole)&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;BigConfig is powerful as a library, but I’ve faced a hard truth: very few developers will learn a language like &lt;a href=&quot;https://clojure.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Clojure &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; just to use a library. However, history shows that developers will learn a new language if it solves a fundamental deployment problem.&lt;/p&gt;
&lt;p&gt;People learned Ruby to master &lt;a href=&quot;https://brew.sh/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Homebrew &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;; they learn &lt;a href=&quot;https://nixos.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Nix &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; for reproducible builds. Meanwhile, tools like &lt;a href=&quot;https://helm.sh/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Helm &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; force users to juggle the awkward marriage of YAML and Go templates—a “solution” many endure only because no better alternative exists. To get developers to cross the language barrier, you have to offer more than a tool; you have to offer a total solution.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-package-manager-epiphany&quot;&gt;The “Package Manager” Epiphany&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;I noticed a significant shift in engagement depending on how I framed the project. When I describe BigConfig as a library, it feels abstract—like “more work” added to a developer’s plate. When I introduce it as a package manager, the interest is immediate.&lt;/p&gt;
&lt;p&gt;In the mind of a developer, a library is a component you have to manage. A package manager is the system that manages things for you. By shifting the perspective, BigConfig goes from being a “Clojure utility” to an “Infrastructure Orchestrator.”&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;how-bigconfig-differs&quot;&gt;How BigConfig Differs&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Like Nix and &lt;a href=&quot;https://guix.gnu.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Guix &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;
, BigConfig embraces a full programming language. However, it avoids the “two-language architecture” common in those ecosystems—where you often have a compiled language for the CLI and a separate interpreted one for the user.&lt;/p&gt;
&lt;p&gt;BigConfig is Clojure all the way down (in the spirirt of Emacs). This allows it to support three distinct environments seamlessly:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The REPL:&lt;/strong&gt; For interactive development and real-time exploration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Shell:&lt;/strong&gt; For traditional CLI workflows and CI/CD pipelines.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Library:&lt;/strong&gt; For embedding directly into your own control planes or APIs.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Beyond the language, BigConfig introduces robust client-side coordination, featuring an Atlantis-style locking mechanism that uses GitHub tags to prevent developer collisions in shared environments.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;limitless-abstraction&quot;&gt;Limitless Abstraction&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The level of abstraction is where BigConfig truly shines. When you adopt the system, you aren’t locked into a rigid schema; you can adapt the entire engine to your specific needs. Complex tasks—like deploying the same architecture across different hyperscalers—are reduced from massive refactors to simply updating a property. It moves the conversation from &lt;em&gt;how&lt;/em&gt; to deploy to &lt;em&gt;what&lt;/em&gt; to deploy.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-roadmap&quot;&gt;The Roadmap&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The next phase is focused on expanding the ecosystem and making package discovery seamless:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hyperscaler Support:&lt;/strong&gt; Having already added &lt;a href=&quot;https://www.digitalocean.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; DigitalOcean &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;
, &lt;a href=&quot;https://www.hetzner.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Hetzner &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;
, and &lt;a href=&quot;https://www.oracle.com/cloud/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Oracle Cloud &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;
, I am now prioritizing &lt;a href=&quot;https://aws.amazon.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; AWS &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;, &lt;a href=&quot;https://cloud.google.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Google Cloud &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;
, and &lt;a href=&quot;https://azure.microsoft.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Azure &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;
.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Application Packages:&lt;/strong&gt; While the first “app”—a remote development environment—is a niche use case, I’m expanding into high-demand stacks like &lt;a href=&quot;https://airflow.apache.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Airflow &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;
and &lt;a href=&quot;https://redplanetlabs.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Rama &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;
.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Ecosystem:&lt;/strong&gt; I am currently defining the formal package manifest and building a registry where users can discover, version, and publish their own infrastructure packages.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The evolution of BigConfig is a testament to the idea that the right abstraction is just as important as the right code. By reframing the tool from a utility you have to manage into a system that manages for you, we bridge the gap between complex cloud resources and developer productivity.&lt;/p&gt;
&lt;p&gt;As we expand our hyperscaler support and formalize our package registry, the goal remains the same: to move infrastructure management away from the “how” and toward the “what.” Whether you are deploying a niche remote environment or a massive data stack like a Data Lake, BigConfig provides the language and the logic to make your infrastructure as versionable and reproducible as your software.&lt;/p&gt;
&lt;p&gt;Would you like to have a follow-up on this topic? What are your thoughts? I’d love to &lt;a href=&quot;https://www.albertomiorin.com/contact#form&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; hear &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; your experiences.&lt;/p&gt;</content:encoded><category>package manager</category><category>bigconfig</category><category>clojure</category><category>babashka</category><category>nix</category><category>guix</category><category>helm</category></item><item><title>Simple over easy for operations</title><link>https://www.bigconfig.it/blog/simple-over-easy-for-operations/</link><guid isPermaLink="true">https://www.bigconfig.it/blog/simple-over-easy-for-operations/</guid><description>While &quot;easy&quot; tools often promise quick starts by mimicking familiar backend languages, they frequently buckle under the non-linear complexity of real-world infrastructure operations. This article explores why building a custom, data-driven workflow engine in Clojure—leveraging immutability, qualified keywords, and a robust REPL—outperforms &quot;easy&quot; alternatives for orchestrating tools like Terraform and Ansible. By prioritizing a simple architectural foundation over the easy path of standard CI/CD scripts, we gain the ability to handle complex branching, resolve state isolation through nested options, and achieve instantaneous debugging that &quot;duct-tape&quot; solutions simply cannot provide.

</description><pubDate>Wed, 11 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Building a workflow engine for infrastructure operations is not trivial. Most people start with a simple mental model: a desired state and a sequence of functions that produce side effects. In Clojure, this looks like a simple thread-first macro:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; {}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fn1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fn2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;&quot;&gt; {}    fn1    fn2    ...)&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Your state &lt;code dir=&quot;auto&quot;&gt;{}&lt;/code&gt; is threaded through &lt;code dir=&quot;auto&quot;&gt;fn1&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;fn2&lt;/code&gt;. However, real-world operations are rarely linear. They require complex branching, error handling, and conditional jumps (e.g., “if success, continue; otherwise, jump to cleanup”).&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;wiring-the-engine&quot;&gt;Wiring the Engine&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;To handle non-linear flows, we associate functions with &lt;strong&gt;qualified keywords&lt;/strong&gt; (steps). Together with the next step, they form the “wiring”. You can override sequential execution by providing a &lt;code dir=&quot;auto&quot;&gt;next-fn&lt;/code&gt; to handle custom branching.&lt;/p&gt;
&lt;p&gt;The core execution loop looks like this:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;loop&lt;/span&gt;&lt;span&gt; [step first-step&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;opts opts]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; [[f next-step] (&lt;/span&gt;&lt;span&gt;wire-fn&lt;/span&gt;&lt;span&gt; step step-fns)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;new-opts (&lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt; opts)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[next-step next-opts] (&lt;/span&gt;&lt;span&gt;next-fn&lt;/span&gt;&lt;span&gt; step next-step new-opts)]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; next-step&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;recur&lt;/span&gt;&lt;span&gt; next-step next-opts)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;next-opts)))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;workflow-example&quot;&gt;Workflow Example&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Here is how we use this engine to create a client-side lock for Terraform using Git tags. The &lt;code dir=&quot;auto&quot;&gt;opts&lt;/code&gt; map represents our “World State”, shared across all functions.&lt;/p&gt;
&lt;p&gt;We invoke it like this: &lt;code dir=&quot;auto&quot;&gt;(lock [] {})&lt;/code&gt;. The first argument is a list of middleware-style step functions, and the second is the starting state.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;-&gt;workflow&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt;:first-step&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;::generate-lock-id&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;             &lt;/span&gt;&lt;span&gt;:wire-fn&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span&gt; [step _]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; step&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                          &lt;/span&gt;&lt;span&gt;::generate-lock-id&lt;/span&gt;&lt;span&gt; [generate-lock-id &lt;/span&gt;&lt;span&gt;::delete-tag&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                          &lt;/span&gt;&lt;span&gt;::delete-tag&lt;/span&gt;&lt;span&gt; [delete-tag &lt;/span&gt;&lt;span&gt;::create-tag&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                          &lt;/span&gt;&lt;span&gt;::create-tag&lt;/span&gt;&lt;span&gt; [create-tag &lt;/span&gt;&lt;span&gt;::push-tag&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                          &lt;/span&gt;&lt;span&gt;::push-tag&lt;/span&gt;&lt;span&gt; [push-tag &lt;/span&gt;&lt;span&gt;::get-remote-tag&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                          &lt;/span&gt;&lt;span&gt;::get-remote-tag&lt;/span&gt;&lt;span&gt; [(&lt;/span&gt;&lt;span&gt;comp&lt;/span&gt;&lt;span&gt; get-remote-tag delete-tag) &lt;/span&gt;&lt;span&gt;::read-tag&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                          &lt;/span&gt;&lt;span&gt;::read-tag&lt;/span&gt;&lt;span&gt; [read-tag &lt;/span&gt;&lt;span&gt;::check-tag&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                          &lt;/span&gt;&lt;span&gt;::check-tag&lt;/span&gt;&lt;span&gt; [check-tag &lt;/span&gt;&lt;span&gt;::end&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                          &lt;/span&gt;&lt;span&gt;::end&lt;/span&gt;&lt;span&gt; [identity]))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;             &lt;/span&gt;&lt;span&gt;:next-fn&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span&gt; [step next-step opts]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; step&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                          &lt;/span&gt;&lt;span&gt;::end&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;nil&lt;/span&gt;&lt;span&gt; opts]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                          &lt;/span&gt;&lt;span&gt;::push-tag&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;choice&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt;:on-success&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;::end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                              &lt;/span&gt;&lt;span&gt;:on-failure&lt;/span&gt;&lt;span&gt; next-step&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                              &lt;/span&gt;&lt;span&gt;:opts&lt;/span&gt;&lt;span&gt; opts})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                          &lt;/span&gt;&lt;span&gt;::delete-tag&lt;/span&gt;&lt;span&gt; [next-step opts]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;choice&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt;:on-success&lt;/span&gt;&lt;span&gt; next-step&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                   &lt;/span&gt;&lt;span&gt;:on-failure&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;::end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                   &lt;/span&gt;&lt;span&gt;:opts&lt;/span&gt;&lt;span&gt; opts})))})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;&quot;&gt;workflow {:first-step ::generate-lock-id             :wire-fn (fn [step _]                        (case step                          ::generate-lock-id [generate-lock-id ::delete-tag]                          ::delete-tag [delete-tag ::create-tag]                          ::create-tag [create-tag ::push-tag]                          ::push-tag [push-tag ::get-remote-tag]                          ::get-remote-tag [(comp get-remote-tag delete-tag) ::read-tag]                          ::read-tag [read-tag ::check-tag]                          ::check-tag [check-tag ::end]                          ::end [identity]))             :next-fn (fn [step next-step opts]                        (case step                          ::end [nil opts]                          ::push-tag (choice {:on-success ::end                                              :on-failure next-step                                              :opts opts})                          ::delete-tag [next-step opts]                          (choice {:on-success next-step                                   :on-failure ::end                                   :opts opts})))})&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;debugging-made-simple&quot;&gt;Debugging Made Simple&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;In many CI/CD systems, debugging is a nightmare of “print” statements and re-running 10-minute pipelines. Because Clojure data structures are immutable and persistent, we can use a &lt;code dir=&quot;auto&quot;&gt;debug&lt;/code&gt; macro provided by BigConfig and a “spy” function to inspect the state at &lt;em&gt;every&lt;/em&gt; step.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;comment&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;debug&lt;/span&gt;&lt;span&gt; tap-values&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt; [(&lt;/span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span&gt; [f step opts]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;               &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;tap&gt;&lt;/span&gt;&lt;span&gt; [step opts]) &lt;/span&gt;&lt;span&gt;;; &quot;Spy&quot; on every state change&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;               &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt; step opts))]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;::bc/env&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:repl&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;             &lt;/span&gt;&lt;span&gt;::tools/tofu-opts&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;workflow/parse-args&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;render&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;             &lt;/span&gt;&lt;span&gt;::tools/ansible-opts&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;workflow/parse-args&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;render&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)})))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;&quot;&gt; [step opts]) ;; &amp;#x22;Spy&amp;#x22; on every state change               (f step opts))]            {::bc/env :repl             ::tools/tofu-opts (workflow/parse-args &amp;#x22;render&amp;#x22;)             ::tools/ansible-opts (workflow/parse-args &amp;#x22;render&amp;#x22;)})))&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Using &lt;code dir=&quot;auto&quot;&gt;tap&gt;&lt;/code&gt;, you get the result “frozen in time”. You can render templates and inspect them without ever executing a side effect.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;solving-the-composability-problem-nested-options&quot;&gt;Solving the Composability Problem: Nested Options&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Operations often require calling the same sub-workflow multiple times. If every workflow uses the same top-level keys, they clash. We solve this with &lt;strong&gt;Nested Options&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;By using the workflow’s namespace as a key, we isolate state. However, sometimes a child needs data from a sibling (e.g., Ansible needs an IP address generated by Terraform). We use an &lt;code dir=&quot;auto&quot;&gt;opts-fn&lt;/code&gt; to map these values explicitly at runtime.&lt;/p&gt;
&lt;p&gt;The specialized &lt;code dir=&quot;auto&quot;&gt;-&gt;workflow*&lt;/code&gt; constructor uses this &lt;code dir=&quot;auto&quot;&gt;next-fn&lt;/code&gt; to manage this state isolation:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span&gt; [step next-step {&lt;/span&gt;&lt;span&gt;:keys&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;::bc/exit&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;:as&lt;/span&gt;&lt;span&gt; opts}]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;steps-set&lt;/span&gt;&lt;span&gt; step)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;swap!&lt;/span&gt;&lt;span&gt; opts* merge (&lt;/span&gt;&lt;span&gt;select-keys&lt;/span&gt;&lt;span&gt; opts [&lt;/span&gt;&lt;span&gt;::bc/exit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;::bc/err&lt;/span&gt;&lt;span&gt;]))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;swap!&lt;/span&gt;&lt;span&gt; opts* assoc step opts))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;reset!&lt;/span&gt;&lt;span&gt; opts* opts))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;cond&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; step &lt;/span&gt;&lt;span&gt;::end&lt;/span&gt;&lt;span&gt;) [&lt;/span&gt;&lt;span&gt;nil&lt;/span&gt;&lt;span&gt; @opts*]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; exit &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)     [&lt;/span&gt;&lt;span&gt;::end&lt;/span&gt;&lt;span&gt; @opts*] &lt;/span&gt;&lt;span&gt;;; Error handling jump&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;:else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[next-step (&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; [[new-opts opts-fn]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt; step-&gt;opts-and-opts-fn next-step [@opts* identity])]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                 &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;opts-fn&lt;/span&gt;&lt;span&gt; new-opts))]))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;&quot;&gt; exit 0)     [::end @opts*] ;; Error handling jump    :else    [next-step (let [[new-opts opts-fn]                     (get step-&gt;opts-and-opts-fn next-step [@opts* identity])]                 (opts-fn new-opts))]))&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This logic ensures that if a step is a sub-workflow, its internal state is captured within the parent’s state under its own key. The &lt;code dir=&quot;auto&quot;&gt;opts-fn&lt;/code&gt; allows us to bridge the gap—for instance, pulling a Terraform-generated IP address into the Ansible configuration dynamically.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-working-directory-and-the-maven-diamond-problem&quot;&gt;The Working Directory and the Maven Diamond Problem&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;In operations, you must render configuration files before invoking tools. If you compose multiple workflows, you run into the “Maven Diamond Problem”: two different parent workflows sharing the same sub-workflow. To prevent them from overwriting each other’s files, we use dynamic, hashed prefixes for working directories:&lt;/p&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;.dist/default-f704ed4d/io/github/amiorin/alice/tools/ansible&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The hash &lt;code dir=&quot;auto&quot;&gt;f704ed4d&lt;/code&gt; is dynamic. If a workflow is moved or re-composed, the hash changes, ensuring total isolation during template rendering.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;conclusion-simple-over-easy&quot;&gt;Conclusion: Simple over Easy&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Tools like &lt;a href=&quot;https://aws.amazon.com/step-functions/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; AWS Step Functions &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;, &lt;a href=&quot;https://temporal.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Temporal &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;, or &lt;a href=&quot;https://www.restate.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Restate &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; are powerful workflow engines, but for many operational tasks, they are not a good fit. &lt;strong&gt;BigConfig&lt;/strong&gt; has an edge because it is local and synchronous where it counts. It turns infrastructure into a local control loop orchestrating multiple tools.&lt;/p&gt;
&lt;p&gt;In the industry, “Easy” (using the same language as the backend, like Go) often wins over “Simple”. But Go lacks a REPL, immutable data structures, and the ability to implement a &lt;code dir=&quot;auto&quot;&gt;debug&lt;/code&gt; macro that allows for instantaneous feedback.&lt;/p&gt;
&lt;p&gt;Infrastructure eventually becomes a mess of “duct tape and prayers” when the underlying tools aren’t built for complexity. If you choose &lt;strong&gt;Simple&lt;/strong&gt; over &lt;strong&gt;Easy&lt;/strong&gt;, Clojure is the best language for operations—even if you’re learning Clojure for the first time.&lt;/p&gt;
&lt;p&gt;Would you like to have a follow-up on this topic? What are your thoughts? I’d love to &lt;a href=&quot;https://www.albertomiorin.com/contact#form&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; hear &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; your experiences.&lt;/p&gt;

&lt;div&gt; &lt;iframe src=&quot;https://www.youtube.com/embed/FHW8b4HQ_Og?si=mvswUjnTp-t7hbe5&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt; &lt;/div&gt;</content:encoded><category>bigconfig</category><category>clojure</category><category>terraform</category><category>ansible</category><category>aws step functions</category><category>temporal</category><category>restate</category><category>go</category><category>simple</category><category>easy</category></item><item><title>Composability: Orchestrating Infrastructure with Babashka and BigConfig Package</title><link>https://www.bigconfig.it/blog/composability-orchestrating-infrastructure-with-babashka-and-bigconfig-package/</link><guid isPermaLink="true">https://www.bigconfig.it/blog/composability-orchestrating-infrastructure-with-babashka-and-bigconfig-package/</guid><description>Stop wrestling with fragmented DevOps scripts. Learn how to use the BigConfig Package and Babashka to build unified, chainable workflows that bridge the gap between OpenTofu, Ansible, and custom Clojure automation.

</description><pubDate>Tue, 10 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In the world of Clojure and DevOps, &lt;a href=&quot;https://babashka.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Babashka &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; has become the gold standard for fast, scripted automation. Today, we’re exploring how to leverage the &lt;a href=&quot;https://www.bigconfig.it/marketplace&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; BigConfig Package &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; with Babashka to create highly composable infrastructure workflows that are as flexible as they are powerful.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;getting-started&quot;&gt;&lt;strong&gt;Getting Started&lt;/strong&gt;&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;One of BigConfig Package’s greatest strengths is its CLI-first approach. You can invoke the &lt;code dir=&quot;auto&quot;&gt;walter&lt;/code&gt; package directly from your terminal to run complex sequences in a single line. For example, to cycle through an environment lifecycle:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;bb&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;walter&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;delete&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;delete&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;bb&lt;/strong&gt;: The Babashka runtime.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;walter&lt;/strong&gt;: The specific task defined in your configuration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;create/delete&lt;/strong&gt;: Individual steps within the workflow. You can chain these steps in any order or frequency required by your pipeline.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;configuration&quot;&gt;&lt;strong&gt;Configuration&lt;/strong&gt;&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;To integrate &lt;code dir=&quot;auto&quot;&gt;walter&lt;/code&gt; package into your project, define your dependencies and tasks in your &lt;code dir=&quot;auto&quot;&gt;bb.edn&lt;/code&gt; file. This acts as the central configuration for your automation.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;bb.edn&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;:deps&lt;/span&gt;&lt;span&gt; {io.github.amiorin/walter {&lt;/span&gt;&lt;span&gt;:git/sha&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;4ea205299cf34c29fa289eacb512a31bfe604d93&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;}}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:tasks&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;:requires&lt;/span&gt;&lt;span&gt; ([io.github.amiorin.walter.package &lt;/span&gt;&lt;span&gt;:as&lt;/span&gt;&lt;span&gt; package]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;             &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[io.github.amiorin.walter.tools &lt;/span&gt;&lt;span&gt;:as&lt;/span&gt;&lt;span&gt; walter]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;             &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[io.github.amiorin.alice.tools &lt;/span&gt;&lt;span&gt;:as&lt;/span&gt;&lt;span&gt; alice])&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tofu {&lt;/span&gt;&lt;span&gt;:doc&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Render and apply OpenTofu (Terraform) plans&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;:task&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;walter/tofu*&lt;/span&gt;&lt;span&gt; *command-line-args* (&lt;/span&gt;&lt;span&gt;package/walter-opts&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt;:big-config.workflow/params&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt;:hyperscaler&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;oci&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;}}))}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ansible {&lt;/span&gt;&lt;span&gt;:doc&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Run Ansible playbooks&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;           &lt;/span&gt;&lt;span&gt;:task&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;walter/ansible*&lt;/span&gt;&lt;span&gt; *command-line-args* (&lt;/span&gt;&lt;span&gt;package/walter-opts&lt;/span&gt;&lt;span&gt; {}))}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ansible-local {&lt;/span&gt;&lt;span&gt;:doc&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Run local Ansible playbooks via the &apos;alice&apos; package&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                 &lt;/span&gt;&lt;span&gt;:task&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;alice/ansible-local*&lt;/span&gt;&lt;span&gt; *command-line-args* (&lt;/span&gt;&lt;span&gt;package/walter-opts&lt;/span&gt;&lt;span&gt; {}))}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;walter {&lt;/span&gt;&lt;span&gt;:doc&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Primary Walter workflow&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;:task&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;package/walter*&lt;/span&gt;&lt;span&gt; *command-line-args* {&lt;/span&gt;&lt;span&gt;:big-config.workflow/params&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt;:hyperscaler&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;oci&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;}})}}}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;key-components&quot;&gt;&lt;strong&gt;Key Components&lt;/strong&gt;&lt;/h3&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code dir=&quot;auto&quot;&gt;:deps&lt;/code&gt;&lt;/strong&gt;: Targets the specific Git SHA of the &lt;code dir=&quot;auto&quot;&gt;walter&lt;/code&gt; package for reproducible builds.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Workflow Structure&lt;/strong&gt;: A package typically splits logic between &lt;code dir=&quot;auto&quot;&gt;package.clj&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;tools.clj&lt;/code&gt;. The former composes multiple tools—like OpenTofu and Ansible—into a unified, high-level workflow.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;parameters-and-composability&quot;&gt;&lt;strong&gt;Parameters and Composability&lt;/strong&gt;&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code dir=&quot;auto&quot;&gt;walter&lt;/code&gt; task utilizes a primary parameter: &lt;code dir=&quot;auto&quot;&gt;:hyperscaler&lt;/code&gt;. Currently, the package offers out-of-the-box support for &lt;strong&gt;Oracle Cloud (OCI)&lt;/strong&gt; and &lt;strong&gt;Hetzner&lt;/strong&gt;.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;uniformity-across-workflows&quot;&gt;&lt;strong&gt;Uniformity across workflows&lt;/strong&gt;&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;The function &lt;code dir=&quot;auto&quot;&gt;(package/walter-opts ...)&lt;/code&gt; is the “glue” of the system. It handles two critical requirements:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Directory Mapping&lt;/strong&gt;: Every tool needs a specific location to render configuration files. This function ensures these are correctly mapped based on the parent workflow.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Options Consistency&lt;/strong&gt;: By populating the &lt;code dir=&quot;auto&quot;&gt;:big-config.workflow/params&lt;/code&gt;, it ensures that all tasks—whether tool-specific or package-wide—share the same options.&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;h3 id=&quot;cross-package-integration&quot;&gt;&lt;strong&gt;Cross-Package Integration&lt;/strong&gt;&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;BigConfig Package excels at mixing and matching. While &lt;code dir=&quot;auto&quot;&gt;tofu&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;ansible&lt;/code&gt; are defined inside the &lt;code dir=&quot;auto&quot;&gt;walter&lt;/code&gt; package, you can seamlessly pull in tasks like &lt;code dir=&quot;auto&quot;&gt;ansible-local&lt;/code&gt; from entirely different packages (such as &lt;code dir=&quot;auto&quot;&gt;alice&lt;/code&gt;).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Developer Tip:&lt;/strong&gt; Even when nesting workflows inside a larger process, you retain the ability to invoke sub-workflows interactively. This makes debugging specific infrastructure segments significantly faster.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div&gt;&lt;h2 id=&quot;conclusion&quot;&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;By transforming standard Terraform and Ansible code into a managed workflow, you gain the ability to invoke resources multiple times with surgical precision. Since the only persistent state is the Terraform state (ideally stored in an S3 backend), your entire team can operate the same resources from different machines.&lt;/p&gt;
&lt;p&gt;Beyond standard deployment, BigConfig Package provides built-in coordination steps like &lt;code dir=&quot;auto&quot;&gt;git-check&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;git-push&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;lock&lt;/code&gt;, and &lt;code dir=&quot;auto&quot;&gt;unlock-any&lt;/code&gt;. These utilities ensure that whether you are working in a tool-specific workflow or a massive package-wide deployment, your team remains in sync and your iing and configuration management, BigConfig Package removes the friction typically found in multi-cloud deployments. This modular anfrastructure remains stable.&lt;/p&gt;
&lt;p&gt;Would you like to have a follow-up on this topic? What are your thoughts? I’d love to &lt;a href=&quot;https://www.albertomiorin.com/contact#form&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; hear &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; your experiences.&lt;/p&gt;
&lt;div&gt; &lt;span&gt; &lt;a href=&quot;https://www.bigconfig.it/walter&quot;&gt; &lt;span&gt;BigConfig Package Walter&lt;/span&gt; &lt;/a&gt; &lt;span&gt;Fork this package or use the package template.&lt;/span&gt; &lt;/span&gt; &lt;svg aria-hidden=&quot;true&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot;&gt;&lt;path d=&quot;M17.92 11.62a1.001 1.001 0 0 0-.21-.33l-5-5a1.003 1.003 0 1 0-1.42 1.42l3.3 3.29H7a1 1 0 0 0 0 2h7.59l-3.3 3.29a1.002 1.002 0 0 0 .325 1.639 1 1 0 0 0 1.095-.219l5-5a1 1 0 0 0 .21-.33 1 1 0 0 0 0-.76Z&quot; /&gt;&lt;/svg&gt; &lt;/div&gt; </content:encoded><category>bigconfig</category><category>bigconfig package</category><category>clojure</category><category>walter</category><category>terraform</category><category>ansible</category></item><item><title>Universal Infrastructure: Solving the Portability Gap with BigConfig</title><link>https://www.bigconfig.it/blog/universal-infrastructure-solving-the-portability-gap-with-bigconfig/</link><guid isPermaLink="true">https://www.bigconfig.it/blog/universal-infrastructure-solving-the-portability-gap-with-bigconfig/</guid><description>Terraform and Ansible often struggle with true cross-provider portability. BigConfig Package bridges this gap, allowing developers to deploy both stateful and stateless applications seamlessly across any hyperscaler, from Oracle Cloud to Hetzner.

</description><pubDate>Mon, 09 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The primary challenge with Terraform and Ansible has always been portability. It is notoriously difficult to take a solution written for one environment and apply it to another without significant manual adjustments. Kubernetes achieved its massive success by leveling this playing field. With tools like Helm, you have a package manager that allows you to install applications without worrying about whether you are on-prem or using a specific hyperscaler.&lt;/p&gt;
&lt;p&gt;However, in the world of Kubernetes, a common sentiment is to avoid stateful applications like databases unless you have mastered every technical nuance. Most internal platforms use Kubernetes for stateless services while relying on managed databases provided by the hyperscaler.&lt;/p&gt;
&lt;p&gt;BigConfig Package changes this dynamic by enabling the creation of universal applications that are both stateful and stateless. &lt;a href=&quot;https://github.com/amiorin/walter&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Walter &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;, the first application built with BigConfig, demonstrates this by deploying seamlessly across Oracle Cloud and Hetzner.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-portability-challenge&quot;&gt;The Portability Challenge&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Infrastructure is rarely uniform. When moving between providers, you encounter several inconsistencies:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The IP address property is named differently depending on the provider.&lt;/li&gt;
&lt;li&gt;The default SSH user varies.&lt;/li&gt;
&lt;li&gt;The UID of the default user is often inconsistent.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The solution involves defining a standardized schema for output parameters in every &lt;code dir=&quot;auto&quot;&gt;main.tf&lt;/code&gt; file:&lt;/p&gt;
  
&lt;div&gt;&lt;h2 id=&quot;gluing-infrastructure-to-configuration&quot;&gt;Gluing Infrastructure to Configuration&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;This approach effectively glues the OpenTofu infrastructure step to the Ansible configuration step. By parsing the JSON output from the infrastructure layer, we can pass critical connection data directly into the workflow:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;defn&lt;/span&gt;&lt;span&gt; opts-fn&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[opts]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; [dir (&lt;/span&gt;&lt;span&gt;workflow/path&lt;/span&gt;&lt;span&gt; opts &lt;/span&gt;&lt;span&gt;::tools/tofu&lt;/span&gt;&lt;span&gt;)]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;merge-with&lt;/span&gt;&lt;span&gt; merge opts {&lt;/span&gt;&lt;span&gt;::workflow/params&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;fs/exists?&lt;/span&gt;&lt;span&gt; dir)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                                                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;p/shell&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt;:dir&lt;/span&gt;&lt;span&gt; dir&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                                              &lt;/span&gt;&lt;span&gt;:out&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:string&lt;/span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;tofu output --json&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                                    &lt;/span&gt;&lt;span&gt;:out&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                                                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;json/parse-string&lt;/span&gt;&lt;span&gt; keyword)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                                                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;-&gt;&gt;&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;s/select-one&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;:params&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:value&lt;/span&gt;&lt;span&gt;])))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                                                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;:ip&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;192.168.0.1&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                                 &lt;/span&gt;&lt;span&gt;:sudoer&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;ubuntu&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;})})))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;&quot;&gt; (p/shell {:dir dir                                                              :out :string} &amp;#x22;tofu output --json&amp;#x22;)                                                    :out                                                    (json/parse-string keyword)                                                    (-&gt;&gt; (s/select-one [:params :value])))                                                {:ip &amp;#x22;192.168.0.1&amp;#x22;                                                 :sudoer &amp;#x22;ubuntu&amp;#x22;})})))&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;As long as the OpenTofu step adheres to the schema by providing an &lt;code dir=&quot;auto&quot;&gt;ip&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;sudoer&lt;/code&gt;, and &lt;code dir=&quot;auto&quot;&gt;uid&lt;/code&gt;, any new hyperscaler can be integrated into this BigConfig Package.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;handling-distribution-differences&quot;&gt;Handling Distribution Differences&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;What about variations in Linux distributions? This can be handled within Ansible or, similar to our Terraform approach, by using different source files based on the distribution.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;defn&lt;/span&gt;&lt;span&gt; tofu&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[step-fns opts]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; [opts (&lt;/span&gt;&lt;span&gt;workflow/prepare&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt;::workflow/name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;::tofu&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                &lt;/span&gt;&lt;span&gt;::render/templates&lt;/span&gt;&lt;span&gt; [{&lt;/span&gt;&lt;span&gt;:template&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;keyword-&gt;path&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;::tofu&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                                     &lt;/span&gt;&lt;span&gt;:overwrite&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                                     &lt;/span&gt;&lt;span&gt;:hyperscaler&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;hcloud&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                                     &lt;/span&gt;&lt;span&gt;:transform&lt;/span&gt;&lt;span&gt; [[&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;{{ hyperscaler }}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;]]}]}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                               &lt;/span&gt;&lt;/span&gt;&lt;span&gt;opts)]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;workflow/run-steps&lt;/span&gt;&lt;span&gt; step-fns opts)))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;&quot;&gt;path ::tofu)                                                     :overwrite true                                                     :hyperscaler &amp;#x22;hcloud&amp;#x22;                                                     :transform [[&amp;#x22;{{ hyperscaler }}&amp;#x22;]]}]}                               opts)]    (workflow/run-steps step-fns opts)))&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The power lies in the dynamic folder pathing. By using the template variable &lt;code dir=&quot;auto&quot;&gt;&quot;{{ hyperscaler }}&quot;&lt;/code&gt; for the hyperscaler, the directory containing the infrastructure code becomes dynamic. This allows us to share core Ansible logic while diverging where necessary, ensuring the code remains clean and manageable.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;By standardizing the handshake between infrastructure provisioning and configuration management, BigConfig Package removes the friction typically found in multi-cloud deployments. This modular approach ensures that your automation remains truly portable, allowing stateful workloads to run wherever they are needed most without being locked into a single provider’s ecosystem.&lt;/p&gt;
&lt;p&gt;Would you like to have a follow-up on this topic? What are your thoughts? I’d love to &lt;a href=&quot;https://www.albertomiorin.com/contact#form&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; hear &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; your experiences.&lt;/p&gt;
&lt;div&gt; &lt;span&gt; &lt;a href=&quot;https://www.bigconfig.it/walter&quot;&gt; &lt;span&gt;BigConfig Package Walter&lt;/span&gt; &lt;/a&gt; &lt;span&gt;Fork this package or use the package template.&lt;/span&gt; &lt;/span&gt; &lt;svg aria-hidden=&quot;true&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot;&gt;&lt;path d=&quot;M17.92 11.62a1.001 1.001 0 0 0-.21-.33l-5-5a1.003 1.003 0 1 0-1.42 1.42l3.3 3.29H7a1 1 0 0 0 0 2h7.59l-3.3 3.29a1.002 1.002 0 0 0 .325 1.639 1 1 0 0 0 1.095-.219l5-5a1 1 0 0 0 .21-.33 1 1 0 0 0 0-.76Z&quot; /&gt;&lt;/svg&gt; &lt;/div&gt; </content:encoded><category>bigconfig</category><category>bigconfig package</category><category>helm</category><category>kubernetes</category><category>clojure</category><category>remote development environment</category><category>walter</category><category>terraform</category><category>ansible</category></item><item><title>The YAML Trap: Escaping Greenspun’s Tenth Rule with BigConfig</title><link>https://www.bigconfig.it/blog/the-yaml-trap-escaping-greenspun-s-tenth-rule-with-bigconfig/</link><guid isPermaLink="true">https://www.bigconfig.it/blog/the-yaml-trap-escaping-greenspun-s-tenth-rule-with-bigconfig/</guid><description>Greenspun’s Tenth Rule warns that every complex system eventually reinvents a buggy version of Lisp. From YAML-hell in CI/CD to the &quot;accidental&quot; programming languages of Helm, Terraform and Ansible, the DevOps world is living this prophecy. Discover how shifting from rigid configuration to the power of Clojure and BigConfig can help you escape the trap of ad-hoc infrastructure logic.

</description><pubDate>Thu, 26 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Greenspun’s Tenth Rule is a famous (and delightfully cynical) adage in computer science. While it was born in the era of C and Fortran, it has never been more relevant than it is today in the world of Platform Engineering.&lt;/p&gt;
&lt;p&gt;If you’ve ever felt like your CI/CD pipeline is held together by duct tape, YAML-indentation prayers, and sheer willpower, you’ve lived this rule.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;what-is-greenspuns-tenth-rule&quot;&gt;What is Greenspun’s Tenth Rule?&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;In the early 90s, Philip Greenspun stated:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Any sufficiently complicated C or Fortran program contains an ad-hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The core insight is that once a system reaches a certain level of complexity, it inevitably requires high-level abstraction, automation, and dynamic logic. Instead of starting with a powerful, established language (like Lisp) built for those tasks, developers often “accidentally” reinvent a mediocre version of one using brittle configuration files and makeshift scripts.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-rule-in-the-devops-ecosystem&quot;&gt;The Rule in the DevOps Ecosystem&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;In DevOps, we strive for Infrastructure as Code (IaC). However, because we started with static configuration formats (YAML/JSON) and tried to force them to perform complex logic, we’ve essentially proven Greenspun right.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;1-the-yaml-programming-trap&quot;&gt;1. The YAML “Programming” Trap&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Tools like Terraform, Ansible, Helm, and GitHub Actions began as simple configuration formats. But as users demanded loops, conditionals, and variables, these tools evolved into “accidental” languages.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The Problem:&lt;/strong&gt; You end up writing complex business logic inside strings within a YAML file.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Reality:&lt;/strong&gt; You are using a “bug-ridden implementation” of a real programming language, but without the benefit of a debugger, a compiler, or proper unit testing.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;2-kubernetes-as-a-distributed-lisp&quot;&gt;2. Kubernetes as a Distributed Lisp&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Some architects argue that Kubernetes is the ultimate manifestation of this rule. Its control loop the constant cycle of reconciling desired state vs. actual state mimics the recursive nature of Lisp environments. It is, in essence, a programmable platform designed to manage other programs.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;escaping-the-trap-putting-lisp-back-in-ops&quot;&gt;Escaping the Trap: Putting Lisp Back in Ops&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The industry has invested massive human capital into building Ansible roles, Helm charts, and Terraform modules. We shouldn’t throw them away, but we must stop trying to make them do things they weren’t designed for.&lt;/p&gt;
&lt;p&gt;How do we escape Greenspun’s trap without rebuilding everything from scratch? By assimilating these tools (to borrow a 90s Star Trek reference).&lt;/p&gt;
&lt;p&gt;This is the core design principle of BigConfig. Instead of fighting against limited YAML DSLs, BigConfig uses Clojure a modern, production-grade Lisp to wrap and orchestrate existing tools.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The BigConfig Philosophy:&lt;/strong&gt; Express infrastructure logic with the most powerful dynamic language available, while still leveraging the ecosystem you already have.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div&gt;&lt;h2 id=&quot;why-it-matters-the-power-of-assimilation&quot;&gt;Why it Matters: The Power of Assimilation&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The Tenth Rule is a warning: Don’t reinvent the wheel poorly. If your infrastructure requires complex logic, stop forcing it into a flat config file.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;from-static-files-to-fractal-architecture&quot;&gt;From Static Files to Fractal Architecture&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;While a standard Helm package is limited strictly to Kubernetes, a &lt;a href=&quot;https://www.bigconfig.it/api/package/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; BigConfig package &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; is a Clojure function. Because BigConfig assimilates Ansible and Terraform alongside Helm, it isn’t siloed.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Truly Cloud Native:&lt;/strong&gt; A Kubernetes application that requires specific cloud resources (like an S3 bucket or an RDS instance) can be abstracted into a single, cohesive unit.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;First-Class Functions:&lt;/strong&gt; In BigConfig, everything is a function. This leads to a fractal architecture where every layer from a single container to a multi-region cloud deployment is governed by the same recursive logic: Observe, Diff, and Act.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;ready-to-stop-writing-logic-in-yaml&quot;&gt;Ready to stop writing logic in YAML?&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Operations is a hard problem. YAML is too rigid, and Go is too low-level for rapid infrastructure iteration. While Python and JavaScript are popular, they lack the REPL-driven development flow that makes infrastructure-as-code feel truly interactive.&lt;/p&gt;
&lt;p&gt;Clojure is the most robust Lisp available today and it won’t let you down.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Greenspun’s Tenth Rule isn’t just a witty observation; it’s a technical debt warning. When we try to solve 21st-century infrastructure challenges using static configuration files, we inevitably end up building “shadow” programming languages that are difficult to test, impossible to debug, and fragile to scale.&lt;/p&gt;
&lt;p&gt;By embracing a functional, Lisp-based approach through BigConfig, we stop fighting the limitations of YAML and start leveraging the power of actual logic. Instead of building a “bug-ridden implementation of half of Common Lisp,” we use the real thing Clojure to orchestrate, automate, and scale.&lt;/p&gt;
&lt;p&gt;The goal of Platform Engineering shouldn’t be to write more scripts; it should be to create elegant, recursive systems that can manage themselves. It’s time to move past the duct tape and prayers and give our infrastructure the robust, dynamic foundation it deserves.&lt;/p&gt;
&lt;p&gt;Would you like to have a follow-up on this topic? What are your thoughts? I’d love to &lt;a href=&quot;https://www.albertomiorin.com/contact#form&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; hear &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; your experiences.&lt;/p&gt;</content:encoded><category>lisp</category><category>clojure</category><category>bigconfig</category><category>bigconfig package</category><category>helm</category><category>ansible</category><category>terraform</category><category>kubernetes</category></item><item><title>Introducing BigConfig Package</title><link>https://www.bigconfig.it/blog/introducing-bigconfig-package/</link><guid isPermaLink="true">https://www.bigconfig.it/blog/introducing-bigconfig-package/</guid><description>BigConfig Package moves beyond the &quot;Kubernetes-only&quot; mindset of Helm by treating infrastructure and software as a single unit. By replacing specialized CLIs with Babashka (Clojure), it eliminates the friction between shell scripts and compiled binaries. This approach allows developers to move seamlessly between a CLI, a REPL for debugging, and a library for deep automation.

</description><pubDate>Mon, 23 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Helm has long been the gold standard for Kubernetes, but it hits a wall the moment you step outside the cluster. While there are projects to bridge this gap, they never quite achieved Helm’s level of ubiquity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;BigConfig Package&lt;/strong&gt; is taking a different path. It aims to deliver both infrastructure and software as a unified, cohesive delivery.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-case-against-the-cli-proliferation&quot;&gt;The Case Against the “CLI Proliferation”&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;One of the most radical aspects of BigConfig Package is what it lacks: a dedicated CLI. In BigConfig’s philosophy, a CLI is just another tool that end-users should tailor to their specific needs. We argue that the current explosion of specialized CLIs is actually a symptom of unsolved underlying problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Bash Limitations:&lt;/strong&gt; The fragility of shell scripting forces developers to wrap logic in binaries.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interoperability Gaps:&lt;/strong&gt; Integrating multiple CLI tools via scripting is often easier than managing complex library dependencies, leading to “scripting fatigue.”&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Go Paradox:&lt;/strong&gt; While static languages like Go are excellent for single binary deployments, they force the creation of monolithic tools that make composition more difficult (e.g., Terraform plugin is based on GRPC).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;BigConfig solves these issues by leveraging Clojure and Babashka. By using a dynamic, high-performance language, we replace rigid tools with flexible Babashka tasks, offering the power of a library with the convenience of a script.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-three-environments-shell-repl-and-lib&quot;&gt;The Three Environments: Shell, REPL, and Lib&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Most DevOps professionals only interact with tools (like Helm) in the Shell environment. BigConfig expands this to three distinct planes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Shell:&lt;/strong&gt; For quick, command-line execution.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;REPL:&lt;/strong&gt; For real-time, interactive debugging and experimentation. No more “edit-coffee-fail” loops.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lib:&lt;/strong&gt; For deep automation. You can orchestrate complex scenarios without reimplementing anything when switching gear from CLI tool to in-house solution.&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;h2 id=&quot;what-is-a-bigconfig-package&quot;&gt;What is a BigConfig Package?&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;There is no need to reinvent the wheel. A BigConfig Package is simply a Clojure repository, and the package itself is just a Clojure function written with &lt;a href=&quot;https://www.bigconfig.it/api/workflow/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; BigConfig Workflow &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our first application, &lt;a href=&quot;https://github.com/amiorin/walter&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Walter &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;, tackles a challenge Helm simply wasn’t built for: Remote Development Environments on machines instead of containers.&lt;/p&gt;
&lt;p&gt;While devcontainers are popular, containerizing a full development environment on Kubernetes has significant overhead and limitations. Nix is the superior choice here. Its native multi-user support makes it ideal for high-powered “beefy” machines shared by multiple developers.&lt;/p&gt;
&lt;p&gt;In this setup, Terraform and Ansible do the heavy lifting but remain completely hidden once development is complete, leaving behind a clean interface that doesn’t “leak” its underlying complexity.&lt;/p&gt;
&lt;p&gt;The use case of Remote Development Environment is getting traction and by integrating a Linux user to run a self-hosted GitHub runner, you unlock massive efficiency:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Cost Reduction:&lt;/strong&gt; Run Dev and CI on the same hardware.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Instant Caching:&lt;/strong&gt; CI pipelines never miss a cache hit because the environment is shared.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Absolute Parity:&lt;/strong&gt; “Works on my machine” becomes a reality because the Dev and CI environments are identical.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Native Speed:&lt;/strong&gt; Docker runs natively, eliminating the virtualization overhead found on macOS or Windows.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;comparison-helm-vs-babashka&quot;&gt;Comparison: Helm vs. Babashka&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;the-helm-way&quot;&gt;The Helm Way&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;In this paradigm, you manage repositories and install charts. It typically requires juggling YAML, Go templates, and Bash.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;helm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;repo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bitnami&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://charts.bitnami.com/bitnami&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;helm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;repo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;update&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;helm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;my-redis&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bitnami/redis&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;the-babashka-way&quot;&gt;The Babashka Way&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;With BigConfig, &lt;code dir=&quot;auto&quot;&gt;helm install&lt;/code&gt; becomes &lt;code dir=&quot;auto&quot;&gt;bb redis create&lt;/code&gt;. The CLI is defined by the package creator but can be customized by the user.&lt;/p&gt;
&lt;p&gt;We use &lt;code dir=&quot;auto&quot;&gt;create&lt;/code&gt; instead of &lt;code dir=&quot;auto&quot;&gt;install&lt;/code&gt; in this case because the infrastructure is part of the delivery. Using &lt;code dir=&quot;auto&quot;&gt;bb&lt;/code&gt; instead of &lt;code dir=&quot;auto&quot;&gt;bash&lt;/code&gt; provides a unified environment where everything is written in Clojure, replacing the fragmented mix of Bash, Compiled Go, and YAML.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;;; bb.edn&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;:deps&lt;/span&gt;&lt;span&gt; {io.github.amiorin/redis {&lt;/span&gt;&lt;span&gt;:git/sha&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;eaff6f&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;}}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:tasks&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;:requires&lt;/span&gt;&lt;span&gt; ([redis &lt;/span&gt;&lt;span&gt;:as&lt;/span&gt;&lt;span&gt; redis])&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redis {&lt;/span&gt;&lt;span&gt;:doc&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Provision a Redis instance&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;         &lt;/span&gt;&lt;span&gt;:task&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;comp-wf/redis*&lt;/span&gt;&lt;span&gt; *command-line-args*)}}}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;bb&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;redis&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;bb&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;redis&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;delete&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The era of managing a dozen different CLIs just to deploy a single stack is reaching its breaking point. BigConfig offers a way out, not by building a bigger tool, but by providing a more flexible foundation. By unifying the way we handle infrastructure and applications through Clojure, we aren’t just deploying software; we are creating a more cohesive, reproducible, and developer-friendly ecosystem.&lt;/p&gt;
&lt;p&gt;Would you like to have a follow-up on this topic? What are your thoughts? I’d love to &lt;a href=&quot;https://www.albertomiorin.com/contact#form&quot;&gt;hear&lt;/a&gt; your experiences.&lt;/p&gt;</content:encoded><category>bigconfig package</category><category>helm</category><category>kubernetes</category><category>clojure</category><category>bigconfig</category><category>remote development environment</category><category>walter</category><category>terraform</category><category>ansible</category></item><item><title>My Keyboard Endgame: 34 Keys, 3 Layers, and a Macropad</title><link>https://www.bigconfig.it/blog/my-keyboard-endgame-34-keys-3-layers-and-a-macropad/</link><guid isPermaLink="true">https://www.bigconfig.it/blog/my-keyboard-endgame-34-keys-3-layers-and-a-macropad/</guid><description>I spend a lot of time analyzing other people&apos;s &quot;endgame&quot; setups, and I honestly don&apos;t understand why so many users accept such steep learning curves.

The secret to a sustainable workflow is simplicity. If your layout is so complex that a month away from your desk &quot;drives you nuts&quot; when you return, it’s not an endgame—it’s a chore. Here is how I stripped my setup down to the essentials without losing productivity.

</description><pubDate>Fri, 13 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I spend a lot of time analyzing other people’s “endgame” setups, and I honestly don’t understand why so many users accept such steep learning curves.&lt;/p&gt;
&lt;p&gt;The secret to a sustainable workflow is simplicity. If your layout is so complex that a month away from your desk “drives you nuts” when you return, it’s not an endgame—it’s a chore. Here is how I stripped my setup down to the essentials without losing productivity.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.bigconfig.it/_astro/keyboard.BpyXHFoU_ZRv5kl.webp&quot; alt=&quot;keyboard&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;3742&quot; height=&quot;1001&quot;&gt;&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-hardware-philosophy&quot;&gt;The Hardware Philosophy&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;34 Keys is Enough&lt;/strong&gt;: I’m a &lt;a href=&quot;https://github.com/davidphilipbarr/Sweep&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Ferris Sweep MX &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; devotee. You can buy the the Silakka54 from Aliexpress if you are on a budget.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;2 Thumb Keys&lt;/strong&gt;: You don’t need a cluster; two well-placed keys are sufficient.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Externalize the Complexity&lt;/strong&gt;: Don’t cram multimedia into layers. Use a dedicated macropad like the Elgato Stream Deck (or the budget-friendly Ajazz AKP03E). This gives you physical dials and buttons for the stuff that doesn’t belong on a typing layer.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The Trackpad&lt;/strong&gt;: The Apple Magic Trackpad remains king. I use &lt;a href=&quot;https://mouseless.click/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Mouseless &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; to reduce dependency, but I refuse to add clunky mouse layers to my keyboard.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;the-three-layer-rule&quot;&gt;The Three-Layer Rule&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Most people over-engineer their firmware. I’ve cut out the “magic” (No Macros, No Tap Dance, No Key Overrides) in favor of logic.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Layer 0&lt;/strong&gt;: Alpha. Standard typing.
&lt;img src=&quot;https://www.bigconfig.it/_astro/layer0.Bv0hdh_7_16Emjr.webp&quot; alt=&quot;layer 0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;1404&quot; height=&quot;672&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Layer 1&lt;/strong&gt;: Numbers &amp;amp; Navigation. I use the &lt;code dir=&quot;auto&quot;&gt;zxcv&lt;/code&gt; row for numbers. This leaves 15 keys free for my Tiling Window Manager of choice, &lt;a href=&quot;https://nikitabobko.github.io/AeroSpace/guide&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; AeroSpace &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;.
&lt;img src=&quot;https://www.bigconfig.it/_astro/layer1.CfX62BsE_1Y1Ddz.webp&quot; alt=&quot;layer 1&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;1390&quot; height=&quot;672&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Layer 2&lt;/strong&gt;: Symbols. I don’t understand splitting symbols across layers. There are exactly 29 essential symbols—they fit perfectly on one layer. Keep them together so your brain doesn’t have to hunt.
&lt;img src=&quot;https://www.bigconfig.it/_astro/layer2.vky9CP90_1pVpbh.webp&quot; alt=&quot;layer 2&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;1368&quot; height=&quot;658&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;taming-the-necessary-evils&quot;&gt;Taming the “Necessary Evils”&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Home Row Mods&lt;/strong&gt;: These can be a headache. I’ve contained the “evil” by only using two keys for mods (&lt;code dir=&quot;auto&quot;&gt;Z/X&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;comma/dot&lt;/code&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Combos&lt;/strong&gt;: Only one—&lt;code dir=&quot;auto&quot;&gt;F+J&lt;/code&gt; for &lt;code dir=&quot;auto&quot;&gt;CAPS WORD&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Input Methods&lt;/strong&gt;: Stick to Tap-Hold and Layer Toggle only. Anything else increases the likelihood of misfires.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;the-terminal-centric-stack&quot;&gt;The Terminal-Centric Stack&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;If you live in the terminal, your hardware is only as good as your software’s ability to interpret it. My stack focuses on modern protocols and efficiency:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Protocol&lt;/strong&gt;: Use &lt;a href=&quot;https://github.com/benotn/kkp&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Kitty Keyboard Protocol (kkp) &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; for better key handling.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Terminal&lt;/strong&gt;: &lt;a href=&quot;https://ghostty.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Ghostty &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; (fast, native, modern).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Multiplexer&lt;/strong&gt;: &lt;a href=&quot;https://zellij.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Zellij &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; over tmux.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Editor&lt;/strong&gt;: &lt;a href=&quot;https://github.com/doomemacs/doomemacs&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Doomemacs &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; (Vi bindings forever).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;my-skhd-configuration&quot;&gt;My skhd Configuration&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Since I avoid complex firmware overrides, I handle my application-specific shortcuts via &lt;a href=&quot;https://github.com/asmvik/skhd&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; skhd &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;. This keeps my keyboard “dumb” and my configuration portable.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Use xxd -psd to find the hex code and remove 0a&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Navigation: Prev/Next tab in Chrome&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cmd - i&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;skhd -k &quot;shift + cmd - 0x21&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cmd - o&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;skhd -k &quot;shift + cmd - 0x1E&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Organization: Move tab left/right in Chrome&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;shift + cmd - i&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;skhd -k &quot;shift + ctrl - 0x74&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;shift + cmd - o&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;skhd -k &quot;shift + ctrl - 0x79&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# History: Back and Forward&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cmd - 0x2B&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;skhd -k &quot;cmd - 0x21&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cmd - 0x2F&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;skhd -k &quot;cmd - 0x1E&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Terminal Fix: Emacs in terminal doesn&apos;t support M-[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Mapping M-5 as a workaround&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alt - 0x21&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;skhd -k &quot;alt - 5&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Keep it simple. If you can’t explain your layout to yourself in 30 seconds, it’s too complicated. Externalize your macros to a macropad, keep your symbols on one layer, and let your software do the heavy lifting.&lt;/p&gt;</content:encoded><category>keyboard</category><category>terminal</category><category>productivity</category><category>elgato</category><category>ajazz</category><category>stream deck</category><category>macropad</category></item><item><title>Replacing Integrant and Docker Compose with BigConfig System</title><link>https://www.bigconfig.it/blog/replacing-integrant-and-docker-compose-with-bigconfig-system/</link><guid isPermaLink="true">https://www.bigconfig.it/blog/replacing-integrant-and-docker-compose-with-bigconfig-system/</guid><description>BigConfig System is a Clojure library that unifies application architecture and infrastructure management into a single, programmable workflow, eliminating the friction of switching between the REPL and external tools like Docker Compose.

By treating system lifecycles as a sequence of code-driven functions rather than static YAML configurations, it allows developers to manage complex processes—such as dynamic Postgres initialization and teardown—entirely within a running session.

This &quot;Emacs-style&quot; approach favors high-granularity evaluation and real-time state inspection over heavy-handed namespace refreshes, consolidating the entire development stack into a single Clojure-based source of truth for improved introspection and speed.

</description><pubDate>Tue, 10 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In the Clojure ecosystem, we’ve grown accustomed to a structural divide. We manage our application architecture with libraries like &lt;a href=&quot;https://github.com/weavejester/integrant&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Integrant &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; and our external dependencies (databases, queues, caches) with &lt;a href=&quot;https://docs.docker.com/compose/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Docker Compose &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; or &lt;a href=&quot;https://f1bonacc1.github.io/process-compose/cli/process-compose/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Process Compose &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;While effective, this separation creates a constant “context switch.” Your application lives in the REPL, but its foundation is buried in YAML and EDN files. One requires evaluating expressions; the other requires multiple steps to refresh, start, or stop using Emacs or a Shell.&lt;/p&gt;
&lt;p&gt;To bridge this gap, I’ve been developing BigConfig System. It is a library inside the BigConfig ecosystem that treats system lifecycles as programmable workflows rather than static dependency graphs.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-emacs-philosophy&quot;&gt;The “Emacs” Philosophy&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;I view BigConfig through the lens of the Emacs philosophy: the environment is something you should never have to leave.&lt;/p&gt;
&lt;p&gt;As projects scale, we traditionally reach for external orchestrators. However, from the perspective of a running REPL, they have foreign interfaces. By keeping the orchestration logic strictly within Clojure I’ve unified the application and its dependencies. Now, I can manage the entire stack, from environment variables to database migrations, without ever dropping out of my REPL session.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;solving-the-postgres-dependency&quot;&gt;Solving the Postgres Dependency&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;To test the limits of BigConfig System, I moved away from static Postgres containers in favor of a dynamic, code-driven workflow. I needed to handle multiple profiles (like dev for long-running sessions and test for transient fixtures) simultaneously without port collisions or state pollution.&lt;/p&gt;
&lt;p&gt;Instead of a binary “started/stopped” toggle, BigConfig manages a granular execution sequence:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Environment Prep:&lt;/strong&gt; Dynamically set variables for project-root/.postgres/[profile].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Filesystem Cleanup:&lt;/strong&gt; Ensure a clean slate by wiping stale data from previous runs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Initialization:&lt;/strong&gt; Programmatically execute initdb.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Smart Startup:&lt;/strong&gt; Launch Postgres via babashka/process, grepping logs for the “ready to accept connections” regex.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Provisioning:&lt;/strong&gt; Chain-load createuser, createdb, and sql-migrate.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Teardown:&lt;/strong&gt; A dedicated stop-fn ensures the process tree is destroyed cleanly.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;abandoning-the-refresh-cycle&quot;&gt;Abandoning the “Refresh” Cycle&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Many Clojure developers rely on &lt;code dir=&quot;auto&quot;&gt;cider-ns-refresh&lt;/code&gt;, but for a refined flow, I find it a bit heavy-handed. My development style centers on evaluating expressions directly within &lt;code dir=&quot;auto&quot;&gt;(comment ...)&lt;/code&gt; blocks.&lt;/p&gt;
&lt;p&gt;By using &lt;code dir=&quot;auto&quot;&gt;cider-eval-defun-at-point&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;cider-inspect-last-result&lt;/code&gt;, I gain high-granularity control. The beauty of BigConfig is that the CIDER inspector updates automatically as the system state changes. I can see the system evolve in real-time, directly within my editor, without the jarring reset of a full namespace refresh.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-workflow-in-action&quot;&gt;The Workflow in Action&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Here is how BigConfig System expresses a managed process as a programmable workflow. Notice how the system lifecycle is defined as a series of step functions (&lt;code dir=&quot;auto&quot;&gt;background-process&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;stop&lt;/code&gt;):&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;comment&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; [kill-timeout &lt;/span&gt;&lt;span&gt;150&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;shutdown-timeout &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;clj-timeout &lt;/span&gt;&lt;span&gt;3000&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;;; Define a managed background process with a specific lifecycle&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;;; Start the process in the background and block the main thread&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;;; until the regex is found or the timeout triggers.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;defn&lt;/span&gt;&lt;span&gt; background-process [opts]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; [re-opts (&lt;/span&gt;&lt;span&gt;into&lt;/span&gt;&lt;span&gt; {} [[&lt;/span&gt;&lt;span&gt;:cmd&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;format&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;clj -X big-config.system/main :shutdown-timeout %s&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; shutdown-timeout)]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;:regex&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;#&quot;token&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;:timeout&lt;/span&gt;&lt;span&gt; clj-timeout]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;:key&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;::proc&lt;/span&gt;&lt;span&gt;]])&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;opts (&lt;/span&gt;&lt;span&gt;re-process&lt;/span&gt;&lt;span&gt; re-opts opts)]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;add-stop-fn&lt;/span&gt;&lt;span&gt; opts (&lt;/span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span&gt; [{&lt;/span&gt;&lt;span&gt;:keys&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;::proc&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;:as&lt;/span&gt;&lt;span&gt; opts}]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;when&lt;/span&gt;&lt;span&gt; proc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;destroy!&lt;/span&gt;&lt;span&gt; proc kill-timeout))))))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;;; Define the system as a stateful, wired workflow&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; -&gt;system (&lt;/span&gt;&lt;span&gt;-&gt;workflow&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt;:first-step&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;::start&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                               &lt;/span&gt;&lt;span&gt;:wire-fn&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span&gt; [step _]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                                          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; step&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                            &lt;/span&gt;&lt;span&gt;::start&lt;/span&gt;&lt;span&gt; [background-process &lt;/span&gt;&lt;span&gt;::end&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                            &lt;/span&gt;&lt;span&gt;::end&lt;/span&gt;&lt;span&gt; [stop]))}))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;;; sys1 starts and stops the system. It useful during the development of the system itself.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;;; sys2 starts only. This is useful in all the other cases.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;into&lt;/span&gt;&lt;span&gt; {} [[&lt;/span&gt;&lt;span&gt;:sys1&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;into&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;sorted-map&lt;/span&gt;&lt;span&gt;) (&lt;/span&gt;&lt;span&gt;-&gt;system&lt;/span&gt;&lt;span&gt; [log-step-fn] {&lt;/span&gt;&lt;span&gt;::bc/env&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:repl&lt;/span&gt;&lt;span&gt;}))]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;:sys2&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; [system (&lt;/span&gt;&lt;span&gt;atom&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;into&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;sorted-map&lt;/span&gt;&lt;span&gt;) (&lt;/span&gt;&lt;span&gt;-&gt;system&lt;/span&gt;&lt;span&gt; [log-step-fn] {&lt;/span&gt;&lt;span&gt;::bc/env&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:repl&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                                                                    &lt;/span&gt;&lt;span&gt;::async&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;})))]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;stop!&lt;/span&gt;&lt;span&gt; @system)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@system)]])))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;defn&lt;/span&gt;&lt;span&gt; main [&amp;#x26; {&lt;/span&gt;&lt;span&gt;:keys&lt;/span&gt;&lt;span&gt; [shutdown-timeout]}]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;assert&lt;/span&gt;&lt;span&gt; shutdown-timeout)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;.addShutdownHook&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;Runtime/getRuntime&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Thread.&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span&gt; []&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                               &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;println&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;\n&lt;/span&gt;&lt;span&gt;[Shutdown Hook] Cleaning up resources before exit...&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                               &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Thread/sleep&lt;/span&gt;&lt;span&gt; shutdown-timeout))))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;println&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Script is running... (Press Ctrl+C to stop)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;println&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;token&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@(&lt;/span&gt;&lt;span&gt;promise&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;&quot;&gt;system (-&gt;workflow {:first-step ::start                               :wire-fn (fn [step _]                                          (case step                                            ::start [background-process ::end]                                            ::end [stop]))}))    ;; sys1 starts and stops the system. It useful during the development of the system itself.    ;; sys2 starts only. This is useful in all the other cases.    (into {} [[:sys1 (into (sorted-map) (-&gt;system [log-step-fn] {::bc/env :repl}))]              [:sys2 (let [system (atom (into (sorted-map) (-&gt;system [log-step-fn] {::bc/env :repl                                                                                    ::async true})))]                       (stop! @system)                       @system)]])))(defn main [&amp;#x26; {:keys [shutdown-timeout]}]  (assert shutdown-timeout)  (.addShutdownHook (Runtime/getRuntime)                    (Thread. (fn []                               (println &amp;#x22;\n[Shutdown Hook] Cleaning up resources before exit...&amp;#x22;)                               (Thread/sleep shutdown-timeout))))  (println &amp;#x22;Script is running... (Press Ctrl+C to stop)&amp;#x22;)  (println &amp;#x22;token&amp;#x22;)  @(promise))&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://www.bigconfig.it/_astro/system.CUG25WY3_Z1dFpWY.webp&quot; alt=&quot;emacs&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;4336&quot; height=&quot;1692&quot;&gt;&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;is-it-better-than-integrant-and-docker-compose&quot;&gt;Is it better than Integrant and Docker Compose?&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;It is still early days, and I am currently refining the library. One major advantage is that there is no longer a split between EDN, YAML, and CLJ files—it’s now just a single CLJ file. The shift in developer experience is already palpable. By replacing the fragmented mix of Docker Compose, Process Compose, and Integrant with a unified Clojure-based workflow, I’ve gained a level of introspection and speed that configuration-based tools and libraries simply cannot provide.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;links&quot;&gt;Links&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href=&quot;https://github.com/amiorin/big-config/blob/main/src/clj/big_config/system.clj&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; BigConfig System &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href=&quot;https://github.com/amiorin/rama-jdbc/blob/big-config/test/rama_jdbc/systems.clj&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Postgres example &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href=&quot;https://www.bigconfig.it/libraries/system/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Manual &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;My quest to eliminate configuration files has reached a new milestone with &lt;code dir=&quot;auto&quot;&gt;system.edn&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;compose.yaml&lt;/code&gt;. By committing to a “never leave the REPL” philosophy, I’ve replaced traditional CLI tools and libraries with direct evaluation. While abandoning namespace reloading in favor of evaluating only the final expression isn’t for everyone, it has fundamentally transformed my productivity.&lt;/p&gt;
&lt;p&gt;Would you like to have a follow-up on this topic? What are your thoughts? I’d love to &lt;a href=&quot;https://www.albertomiorin.com/contact#form&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; hear &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; your experiences.&lt;/p&gt;</content:encoded><category>integrant</category><category>docker compose</category><category>process compose</category><category>bigconfig</category><category>system</category><category>bigconfig system</category></item><item><title>Beyond the Shell: Reimagining DevOps with the Clojure REPL</title><link>https://www.bigconfig.it/blog/beyond-the-shell-reimagining-devops-with-the-clojure-repl/</link><guid isPermaLink="true">https://www.bigconfig.it/blog/beyond-the-shell-reimagining-devops-with-the-clojure-repl/</guid><description>Traditional DevOps workflows are often bogged down by the &quot;Edit/Run&quot; loop, requiring developers to constantly switch between editors, directories, and terminal tabs.

BigConfig solves this by leveraging a persistent Clojure REPL to create a unified &quot;Edit/Evaluate&quot; environment.

By orchestrating tools like Terraform through a centralized &quot;workbench map&quot;, BigConfig eliminates context switching and replaces traditional debugging with instant, stateful feedback.

It transforms fragmented operations into a seamless, code-driven flow where infrastructure and development live in a single, interactive workspace.

</description><pubDate>Mon, 02 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We are all familiar with the standard REPL experience provided by shells like Bash, Zsh, or Fish. However, far fewer developers have experienced the power of a Clojure REPL. I built BigConfig in Clojure because I believe we should stop relying on fragmented shell environments for operations and development. By moving to a unified Clojure REPL, we can significantly boost productivity.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-problem-the-editrun-loop&quot;&gt;The Problem: The “Edit/Run” Loop&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Let’s look at the standard workflow for a tool like Terraform:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;cd&lt;/code&gt; into the project root.&lt;/li&gt;
&lt;li&gt;Edit the &lt;code dir=&quot;auto&quot;&gt;.tf&lt;/code&gt; files.&lt;/li&gt;
&lt;li&gt;Run &lt;code dir=&quot;auto&quot;&gt;terraform plan&lt;/code&gt; in the shell.&lt;/li&gt;
&lt;li&gt;Repeat until it works.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now, imagine scaling this across multiple tools. Suddenly, you aren’t just running Terraform; you’re managing &lt;code dir=&quot;auto&quot;&gt;ansible&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;kubectl&lt;/code&gt;, and &lt;code dir=&quot;auto&quot;&gt;helm&lt;/code&gt;. Each tool lives in a different directory. The cognitive cost of switching directories and managing multiple shell sessions becomes a massive bottleneck. You either waste time &lt;code dir=&quot;auto&quot;&gt;cd&lt;/code&gt;-ing back and forth or you end up with twenty terminal tabs, losing track of which is which.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-bigconfig-alternative-editevaluate&quot;&gt;The BigConfig Alternative: “Edit/Evaluate”&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;With BigConfig, this friction disappears. Instead of a “shell-per-directory” approach, we use a single, persistent JVM:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Jack-in once&lt;/strong&gt;: Like a shell, the JVM stays running, but you only need one instance for your entire project.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unified Workspace&lt;/strong&gt;: Clojure files and tool-specific configurations (YAML, HCL, etc.) live in the same project.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Instant Feedback&lt;/strong&gt;: You evaluate Clojure code directly within your editor. No context switching is required.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In the traditional model, “Edit” and “Run” happen in two different programs (the editor and the shell). In BigConfig, “Edit” and “Evaluate” happen in the same program, your editor. Whether you are managing one tool or a hundred, the workflow remains identical.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;workflow-a-new-type-of-flow-control&quot;&gt;Workflow: A New Type of Flow Control&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;To make this possible, BigConfig introduces a specialized flow control: &lt;strong&gt;the workflow.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;While &lt;code dir=&quot;auto&quot;&gt;if&lt;/code&gt; is the classic example of flow control in code, a BigConfig workflow operates on a &lt;strong&gt;workbench map&lt;/strong&gt;. You define a series of &lt;strong&gt;steps&lt;/strong&gt; (specialized functions) that execute sequentially, reading from and writing to this shared map.&lt;/p&gt;
&lt;p&gt;For example, the workbench map might contain a sequence like &lt;code dir=&quot;auto&quot;&gt;[&quot;terraform init&quot;, &quot;terraform plan&quot;]&lt;/code&gt;. A specific step reads those commands and executes them in a directory defined elsewhere in the map. Even if you prefer writing HCL or YAML manually rather than generating it, BigConfig’s workflow engine automates the orchestration, so you don’t have to.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;real-world-application-developing-terraform-providers&quot;&gt;Real-World Application: Developing Terraform Providers&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;I use BigConfig for both operations and core development. Currently, I’m building a &lt;a href=&quot;https://github.com/amiorin/terraform-provider-bigconfig&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Terraform Provider in Clojure &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;. Because the gRPC protocol documentation can be sparse, I built a BigConfig workflow to log and analyze traffic.&lt;/p&gt;
&lt;p&gt;I use two primary workflows for this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The hcloud workflow:&lt;/strong&gt; This starts the Hetzner Terraform Provider in dev mode, launches a gRPC “man-in-the-middle” to log traffic, renders a test &lt;code dir=&quot;auto&quot;&gt;main.tf&lt;/code&gt;, executes Terraform, and transforms the resulting gRPC Java classes into readable Clojure maps.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The dev workflow:&lt;/strong&gt; This is identical to the first, but it swaps the Hetzner provider for my own Clojure-based provider in development.&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; hcloud-wf (&lt;/span&gt;&lt;span&gt;-&gt;workflow&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt;:first-step&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;::start&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                            &lt;/span&gt;&lt;span&gt;:wire-fn&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span&gt; [step step-fns]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                                       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; step&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                         &lt;/span&gt;&lt;span&gt;::start&lt;/span&gt;&lt;span&gt; [start-hcloud &lt;/span&gt;&lt;span&gt;::start-proxy&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                         &lt;/span&gt;&lt;span&gt;::start-proxy&lt;/span&gt;&lt;span&gt; [start-proxy &lt;/span&gt;&lt;span&gt;::prepare&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                         &lt;/span&gt;&lt;span&gt;::prepare&lt;/span&gt;&lt;span&gt; [prepare &lt;/span&gt;&lt;span&gt;::render&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                         &lt;/span&gt;&lt;span&gt;::render&lt;/span&gt;&lt;span&gt; [render/render &lt;/span&gt;&lt;span&gt;::exec&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                         &lt;/span&gt;&lt;span&gt;::exec&lt;/span&gt;&lt;span&gt; [(&lt;/span&gt;&lt;span&gt;partial&lt;/span&gt;&lt;span&gt; run/run-cmds step-fns) &lt;/span&gt;&lt;span&gt;::fix-messages&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                         &lt;/span&gt;&lt;span&gt;::fix-messages&lt;/span&gt;&lt;span&gt; [fix-messages &lt;/span&gt;&lt;span&gt;::end&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                         &lt;/span&gt;&lt;span&gt;::end&lt;/span&gt;&lt;span&gt; [stop]))}))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;comment&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;into&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;sorted-map&lt;/span&gt;&lt;span&gt;) (&lt;/span&gt;&lt;span&gt;hcloud-wf&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt;::bc/env&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:repl&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                 &lt;/span&gt;&lt;span&gt;::test-name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;hcloud&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;})))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; dev-wf (&lt;/span&gt;&lt;span&gt;-&gt;workflow&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt;:first-step&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;::start&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                         &lt;/span&gt;&lt;span&gt;:wire-fn&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span&gt; [step step-fns]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; step&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                      &lt;/span&gt;&lt;span&gt;::start&lt;/span&gt;&lt;span&gt; [start &lt;/span&gt;&lt;span&gt;::start-proxy&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                      &lt;/span&gt;&lt;span&gt;::start-proxy&lt;/span&gt;&lt;span&gt; [start-proxy &lt;/span&gt;&lt;span&gt;::prepare&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                      &lt;/span&gt;&lt;span&gt;::prepare&lt;/span&gt;&lt;span&gt; [prepare &lt;/span&gt;&lt;span&gt;::render&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                      &lt;/span&gt;&lt;span&gt;::render&lt;/span&gt;&lt;span&gt; [render/render &lt;/span&gt;&lt;span&gt;::exec&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                      &lt;/span&gt;&lt;span&gt;::exec&lt;/span&gt;&lt;span&gt; [(&lt;/span&gt;&lt;span&gt;partial&lt;/span&gt;&lt;span&gt; run/run-cmds step-fns) &lt;/span&gt;&lt;span&gt;::fix-messages&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                      &lt;/span&gt;&lt;span&gt;::fix-messages&lt;/span&gt;&lt;span&gt; [fix-messages &lt;/span&gt;&lt;span&gt;::end&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                      &lt;/span&gt;&lt;span&gt;::end&lt;/span&gt;&lt;span&gt; [stop]))}))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;comment&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;into&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;sorted-map&lt;/span&gt;&lt;span&gt;) (&lt;/span&gt;&lt;span&gt;dev-wf&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt;::bc/env&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:repl&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                              &lt;/span&gt;&lt;span&gt;::test-name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;first&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;})))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;&quot;&gt;workflow {:first-step ::start                            :wire-fn (fn [step step-fns]                                       (case step                                         ::start [start-hcloud ::start-proxy]                                         ::start-proxy [start-proxy ::prepare]                                         ::prepare [prepare ::render]                                         ::render [render/render ::exec]                                         ::exec [(partial run/run-cmds step-fns) ::fix-messages]                                         ::fix-messages [fix-messages ::end]                                         ::end [stop]))}))(comment  (into (sorted-map) (hcloud-wf {::bc/env :repl                                 ::test-name &amp;#x22;hcloud&amp;#x22;})))(def dev-wf (-&gt;workflow {:first-step ::start                         :wire-fn (fn [step step-fns]                                    (case step                                      ::start [start ::start-proxy]                                      ::start-proxy [start-proxy ::prepare]                                      ::prepare [prepare ::render]                                      ::render [render/render ::exec]                                      ::exec [(partial run/run-cmds step-fns) ::fix-messages]                                      ::fix-messages [fix-messages ::end]                                      ::end [stop]))}))(comment  (into (sorted-map) (dev-wf {::bc/env :repl                              ::test-name &amp;#x22;first&amp;#x22;})))&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;goodbye-debuggers&quot;&gt;Goodbye, Debuggers&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;I don’t need a traditional debugger for this. Because these workflows are just pure functions that transform a map, I can “attach” any value I want to inspect to the workbench map at any step.&lt;/p&gt;
&lt;p&gt;Thanks to Clojure’s &lt;strong&gt;qualified keywords&lt;/strong&gt;, I never have to worry about naming conflicts between different steps. I can see the entire state of my infrastructure and my code in one place, instantly.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Traditional DevOps workflows are often bogged down by the “Edit/Run” loop, requiring developers to constantly switch between editors, directories, and terminal tabs. BigConfig solves this by leveraging a persistent Clojure REPL to create a unified “Edit/Evaluate” environment. By orchestrating tools like Terraform through a centralized “workbench map,” BigConfig eliminates context switching and replaces traditional debugging with instant, stateful feedback. It transforms fragmented operations into a seamless, code-driven flow where infrastructure and development live in a single, interactive workspace.&lt;/p&gt;
&lt;p&gt;Would you like to have a follow-up on this topic? What are your thoughts? I’d love to &lt;a href=&quot;https://www.albertomiorin.com/contact#form&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; hear &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; your experiences.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;update&quot;&gt;Update&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;I received an interesting question and wanted to share my response here.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;question&quot;&gt;Question&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;The REPL often suffers from reproducibility issues due to out-of-order execution. Does BigConfig address this in some way?&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;answer&quot;&gt;Answer&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;This is addressed both by Clojure itself and within BigConfig’s architecture.&lt;/p&gt;
&lt;p&gt;Clojure provides the foundation because out-of-order execution would be detrimental to any serious development. By using Clojure for operations—rather than Python or Node—we can leverage the same robust patterns used in traditional Clojure REPL-driven development.&lt;/p&gt;
&lt;p&gt;BigConfig specifically solves this by introducing a new control flow: the workflow. A workflow is a function that invokes other functions, called steps, similar to AWS Step Functions (which served as an inspiration for BigConfig). If you are modifying a specific step, such as &lt;code dir=&quot;auto&quot;&gt;prepare&lt;/code&gt;, you would edit it like this:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;comment&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;defn&lt;/span&gt;&lt;span&gt; prepare [opts]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;; you are developing the code of this step&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;into&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;sorted-map&lt;/span&gt;&lt;span&gt;) (&lt;/span&gt;&lt;span&gt;hcloud-wf&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt;::bc/env&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:repl&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                                   &lt;/span&gt;&lt;span&gt;::test-name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;hcloud&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;}))))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;In my editor, I simply press &lt;code dir=&quot;auto&quot;&gt;C-c C-c&lt;/code&gt; to evaluate everything in the correct order. I can then inspect the workbench map in one window and the stdout in another, ensuring a predictable and reproducible state every time.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.bigconfig.it/_astro/repl.BMVmBYla_Z1BwORS.webp&quot; alt=&quot;repl&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;4480&quot; height=&quot;2520&quot;&gt;&lt;/p&gt;</content:encoded><category>shell</category><category>bigconfig</category><category>clojure</category><category>gitops</category><category>terraform</category><category>ansible</category><category>helm</category><category>kubectl</category><category>gRPC</category></item><item><title>Hypermedia: The New/Old Architecture Set to Revolutionize Web and Mobile Development</title><link>https://www.bigconfig.it/blog/hypermedia-the-new-old-architecture-set-to-revolutionize-web-and-mobile-development/</link><guid isPermaLink="true">https://www.bigconfig.it/blog/hypermedia-the-new-old-architecture-set-to-revolutionize-web-and-mobile-development/</guid><description>Hypermedia is the tech everyone is talking about, yet it remains a bit of a mystery for many. After diving deep, I’m convinced this new/old approach is not just a trend—it’s poised to disrupt how we build applications on both the web and mobile.

The strategic shift towards Hypermedia Driven Applications (HDA) fundamentally alters the economics of development, making internal solutions significantly more viable. As the cost of building web and mobile experiences drops, so too does the traditional calculus of &quot;build vs. buy.

To capitalize on this change, our next major step is integrating Internal Developer Portal capabilities directly into BigConfig. This addition completes BigConfig&apos;s transformation into the essential, comprehensive library for scalable operations, providing core building blocks like configuration as code, workflow orchestration, scaffolding, robust persistency, auditing, control planes, and now, internal developer portals.

</description><pubDate>Tue, 18 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Hypermedia is the tech everyone is talking about, yet it remains a bit of a mystery for many. After diving deep, I’m convinced this new/old approach is not just a trend—it’s poised to disrupt how we build applications on both the web and mobile.&lt;/p&gt;
&lt;p&gt;Here are my thoughts, inspired by the powerful concepts driving this shift.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;what-is-a-hypermedia-driven-application-hda&quot;&gt;What is a Hypermedia Driven Application (HDA)?&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The Hypermedia Driven Application (HDA) architecture—championed by projects like &lt;a href=&quot;https://htmx.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; htmx &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;—is an ingenious fusion of old and new.&lt;/p&gt;
&lt;p&gt;It takes the simplicity and flexibility of traditional Multi-Page Applications (MPAs) and marries it with the smooth user experience (UX) typically found only in complex Single-Page Applications (SPAs).&lt;/p&gt;
&lt;p&gt;After struggling to grasp this concept myself, I decided the best way to understand it was to build one. That journey led me to the fascinating world of &lt;a href=&quot;https://github.com/andersmurphy/hyperlith&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Hyperlith  &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; and &lt;a href=&quot;https://data-star.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; Datastar &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt;. The ideas behind Hyperlith are truly revolutionary. Hyperlith stands for Hypermedia and Monolith.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-philosophy-dumb-terminals-smart-backend&quot;&gt;The Philosophy: Dumb Terminals, Smart Backend&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;In the HDA model, your application’s logic, state, and rendering all reside in the backend.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Your distributed application runs entirely on the server.&lt;/li&gt;
&lt;li&gt;The browser or mobile app becomes a dumb terminal.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pages are conceptually replaced by views (for both web and mobile). When a user interacts with a control (like a button), it triggers an action on the server. This action recalculates the current view, which is then pushed to all relevant users.&lt;/p&gt;
&lt;p&gt;Think about a traditional leaderboard implemented as a web page: instead of polling or building complex real-time infrastructure, the view automatically updates for all online users as soon as the data changes on the server. The multiplayer capabilities are practically trivial!&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-disruption-why-hdas-win&quot;&gt;The Disruption: Why HDAs Win&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;This server-side approach has several game-changing implications for development:&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;unified-codebase-for-web-and-mobile&quot;&gt;Unified Codebase for Web and Mobile&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;With frameworks like Hyperlith and Datastar, you can achieve amazing results without starting from scratch. Instead of managing separate SPAs or native mobile apps, you manage a single backend that renders views suitable for any client.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;lightning-fast-mobile-development&quot;&gt;Lightning-Fast Mobile Development&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Mistake a typo in your app? Need to tweak a UI element? In the HDA model, you simply fix the code on the server. You don’t have to upload a new mobile app version and wait for store approval. The changes are live instantly for all users.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;effortless-ab-testing&quot;&gt;Effortless A/B Testing&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Since all rendering happens on the server, implementing server-side A/B testing becomes incredibly simple. You can easily serve different versions of a view to different user segments without complicated client-side routing or code splitting.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;simplified-shared--user-specific-views&quot;&gt;Simplified Shared &amp;#x26; User-Specific Views&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;The HDA architecture makes it easier to create views that are shared among users (like a stock ticker) and then progressively make portions user-specific (like a profile widget).&lt;/p&gt;
&lt;p&gt;It’s easier to start with a shared view and then make it specific than to start with isolated pages and struggle to add shared portions later.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;efficient-updates-and-network-performance&quot;&gt;Efficient Updates and Network Performance&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Many people worry that sending the complete HTML on every change won’t scale, especially on mobile networks. This concern is often misplaced.&lt;/p&gt;
&lt;p&gt;Instead of thinking of it as constant full-page refreshes, imagine it as a stream of bytes, highly compressed with algorithms like Brotli.&lt;/p&gt;
&lt;p&gt;The compression acts as your caching and diffing mechanism, achieving compression ratios of 100 or more! This efficiency is critical: a $1.5 MB/s sustained throughput on the server is equivalent to handling $150 MB/s without compression, drastically reducing infrastructure costs.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;seamless-user-experience-with-dom-morphing&quot;&gt;Seamless User Experience with DOM Morphing&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Modern apps like YouTube and Spotify offer that polished mini-player experience, letting you keep watching content while navigating to a new page. This amazing user experience is a natural fit for HDAs, thanks to a technique called DOM morphing.&lt;/p&gt;
&lt;p&gt;Instead of completely swapping out the entire page (like in a traditional MPA) or relying on complex client-side routing (like in an SPA), the HDA backend sends the new rendered view. The client then intelligently morphs the existing DOM to match the new version. This means that persistent elements—like an ongoing video player or music widget—can retain their place, focus, and internal state, resulting in a smooth, interruption-free user flow.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;bigconfig-and-internal-developer-portals&quot;&gt;BigConfig and Internal Developer portals&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The strategic shift towards Hypermedia Driven Applications (HDA) fundamentally alters the economics of development, making internal solutions significantly more viable. As the cost of building web and mobile experiences drops, so too does the traditional calculus of “build vs. buy.”&lt;/p&gt;
&lt;p&gt;To capitalize on this change, our next major step is integrating Internal Developer Portal capabilities directly into BigConfig. This addition completes BigConfig’s transformation into the essential, comprehensive library for scalable operations, providing core building blocks like configuration as code, workflow orchestration, scaffolding, robust persistency, auditing, control planes, and now, internal developer portals.&lt;/p&gt;
&lt;p&gt;I’m working on the code right now. Stay tuned, I will update the blog post as soon as I have the first version.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;see-it-in-action&quot;&gt;See it in Action&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://www.bigconfig.it/_astro/hda.a9a1Uimx_Z174yKQ.webp&quot; alt=&quot;Alt text&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;4480&quot; height=&quot;2520&quot;&gt;&lt;/p&gt;
&lt;p&gt;I built a simple HDA &lt;a href=&quot;https://example.bigconfig.it/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; example &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; to prove the concept. When you open it in two separate browser tabs and interact with one, the other updates instantly.&lt;/p&gt;
&lt;p&gt;The most incredible part? Zero JavaScript was required to implement the real-time, multiplayer capabilities. The state is managed centrally and seamlessly on the server.&lt;/p&gt;
&lt;p&gt;The future of application development is here, and it looks a lot like the past—just smarter, faster, and much more scalable.&lt;/p&gt;
&lt;p&gt;Would you like to have a follow-up on this topic? What are your thoughts? I’d love to &lt;a href=&quot;https://www.albertomiorin.com/contact#form&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt; hear &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot;&gt;&lt;/path&gt; &lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt; &lt;line x1=&quot;10&quot; x2=&quot;21&quot; y1=&quot;14&quot; y2=&quot;3&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/a&gt; your experiences.&lt;/p&gt;</content:encoded><category>hypermedia</category><category>htmx</category><category>datastar</category><category>hyperlith</category><category>bigconfig</category><category>internal developer platform</category><category>mobile</category><category>web</category></item></channel></rss>