Skip to content
Go back

Why Are Forms So Difficult?

Published:  at  10:58 AM

In software engineering, we’ve largely agreed on something.

Infrastructure? Configuration files. CI pipelines? YAML. Kubernetes? Manifests. GitHub Actions? YAML again. Feature flags? Config. Routing? Config. Permissions? Config.

We describe systems declaratively. We version them. We review them in pull requests. We diff them. We roll them back.

Then we get to forms.

And suddenly we’re dragging boxes around a canvas.


The Strange Exception

Forms are one of the most common surfaces in software. They collect data. They gate access. They trigger workflows. They define onboarding experiences.

They’re not trivial.

And yet, in most stacks, they live outside the codebase — inside a visual builder, with hidden logic, no meaningful diffs, and limited traceability.

Need conditional logic? Click through a flowchart. Need branching navigation? Hope you wired it correctly. Need to see what changed? Compare screenshots.

It’s strange.

We’ve spent a decade pushing toward declarative systems everywhere else. Infrastructure as Code won. CI-as-code won. Policy-as-code won.

But forms are still “builder-as-a-service.”

I kept running into this myself — every side project that needed a form meant choosing between a visual builder that couldn’t quite do what I wanted, or building the whole thing from scratch. I’d need conditional visibility, and the builder could technically do it, but only through a nested settings panel three clicks deep. I’d need to change a field label, and there was no way to see what had changed or when. Neither option felt right.


Are Forms Actually Different?

When you strip away the UI, what is a form?

It’s a declaration.

A list of fields with types, labels, and validation rules. Some routing conditions. Maybe an integration to send data somewhere.

That’s configuration.

There’s nothing inherently visual about it. The visual builder is just an opinionated interface layered on top of a structured definition. You could describe any form as a text file — fields, rules, structure — and lose nothing.

What if we treated forms the same way we treat everything else? What if a form was just a file?

So I tried it.


A Form as Configuration

That’s what Declarative Forms is. A form is a YAML file in a GitHub repo. Push it and it’s live.

This is the smallest possible example:

version: 1
title: "Basic Example"

sections:
  - id: section_1
    fields:
      - id: email
        type: email
        label: What's your email?
        validators:
          - required
    next: done

Push this to GitHub and navigate to app.declarativeforms.com/{org}/{repo}/{form-name} — the form is live. No build step, no deploy, no hosting configuration.

The YAML file is the form. GitHub is the CMS. Git history is your changelog.

Want to change a field label? Edit the file, push, done. Want to review changes before they go live? Open a pull request and read the diff.

Conditional visibility? One line: visible_when: data.citizenship === 'Yes'. Routing between sections? A next property with simple rules. Webhooks? A few lines at the bottom of the file. The examples repo shows how these pieces compose in real forms, including an advanced example with multi-section routing and conditional logic.


The Real Benefit Isn’t YAML

The benefit isn’t that YAML is exciting.

It’s that files fit into an existing workflow:

What I actually noticed after switching wasn’t any single feature — it was that forms stopped feeling like a separate concern. The same muscle memory I use to change infrastructure now applies to an onboarding flow. When something breaks, git blame tells me when it changed. When I want to review a form update, it’s just a diff. When I want to roll something back, it’s git revert. I stopped context-switching between “the codebase” and “the form builder” — because there’s no form builder to switch to.

Forms stop being fragile, external mini-products. They become part of the system — reviewed, versioned, and traceable like everything else. And that shift feels surprisingly significant.


Maybe They Don’t Need to Be Special

We used to manually provision servers. We used to click through CI dashboards. We used to manage permissions in admin UIs. Then, one by one, we decided configuration was better.

When forms live outside the repo, they drift. They’re harder to audit, harder to review, harder to reason about at scale. Nobody notices when a routing rule silently breaks in a form builder — there’s no diff to catch it. When forms are configuration, they inherit the stability of everything else in the stack.

It’s not about replacing visual builders — those are great for many workflows. It’s about whether forms are fundamentally different, or whether we’ve just gotten used to treating them that way.

Forms might just be the last holdout. And maybe they don’t need to be.

If infrastructure can be code, and pipelines can be code, and policy can be code — why can’t forms just be configuration?


Try It

One honest caveat first: this approach assumes you’re comfortable with YAML and live in Git. If indentation rules make you nervous, this isn’t your tool. If you need to hand form creation to a non-technical team, use Tally — it’s great, and it’s built for that workflow. This is a tool built for engineers who’d rather type than click.

Three ways in:

  1. Browse the examples repo — every feature mentioned here has a working example.
  2. See the advanced example live — multi-section, conditional visibility, conditional routing.
  3. Fork the repo, create a YAML file, push it. Your form is live.

I’m not going to pretend this is the future of all forms. But if you’ve ever hit the ceiling of a form builder and found yourself choosing between “live with the limitation” and “build it from scratch” — there’s a third option now. A YAML file, a Git repo, and nothing else.



Next Post
Debugging Berlin’s Housing Market