My software factory emerged that way and for the 50-70% of simpler tasks I work on this saves me so much time and, more importantly, mental overhead.
My software factory emerged that way and for the 50-70% of simpler tasks I work on this saves me so much time and, more importantly, mental overhead.
Using Elixir, Phoenix, LiveView and Ash for everything creates a very well defined and verifiable structure for every part of the codebase with a precommit that looks like this:
precommit: [
"compile --warnings-as-errors", # Fail compilation if any warnings exist
"deps.unlock --check-unused", # Fail if unused dependencies are unlocked
"deps.audit", # Scan dependencies for known security vulnerabilities
"cmd mix hex.audit", # Audit Hex packages for retired or vulnerable versions
"migrations.safety", # Check database migrations for unsafe operations
"format --check-formatted", # Verify code matches the formatter style
"credo --strict", # Run strict static code analysis checks
"sobelow --exit --threshold high --skip", # Fail on high-confidence security issues only
"ash.codegen --check", # Ensure Ash generated code is up to date
"usage_rules.sync --check", # Verify usage rules are synchronized
"cmd bun run lint", # If there's JS beyond LiveView
"test" # Run the full test suite
]
The result has been excellent consistency across my code base and the AI learns fast how it should be working. Everything comes out very readable if I want to dig into something.For the most part, everything I ask for gets 1 shotted with this approach with typically rare minor tweaks and refinements.
I personally want to keep my commits light & fast, so it's not an impediment to commit often. I've always found even a simple reformatter on commit can be a bit annoying. Ideally for every micro-task done I commit, even if it's messy and even if I decide to revert the work later. It becomes kind of like a reasoning trace. Only when the task is completed, must the code comply to all the necessary checks, and then it becomes a PR and CI jobs run to validate
If you commit early and often, yes this could turn annoying really fast. May be GP works with local-changes/git-index for a bit and commits when somewhat satisfied.
1. If these all took a very long time to run, it would create a case to defer to the CI. With Elixir, virtually every check you see runs maximally parallel and the entire precommit stack finishes very quickly. I use this as part of queued, local "/goal" commands because I have built up a level of trust that queuing a dozen goals and then reviewing them in the morning will be correct virtually every time.
2. Since everything finishes quickly, having it setup as a precommit check let's the agent see and correct all of the failures locally before anything goes up to the CI itself. All of these checks are duplicated at the CI layer, but it's extremely rare for anything to fail. As a bonus, the AI learns and adapts to the feedback of these checks so it's correct the first time eventually.