Project Architecture
Goat separates the application definition, generator templates, source code, and project automation from the code produced through generation.
The generator is intended to speed up development rather than lock the user into an imposed generation model.
Therefore, code created by Goat remains regular project source code. You can freely edit, extend, and refactor it. The generator primarily automates repetitive elements, but does not take control away from you over the final implementation.
In practice, you can combine two ways of working:
- use the model and generator where this saves time and ensures consistency,
- edit code directly where the project requires custom logic or a custom solution.
This allows Goat to support the project after its initial generation as well, rather than forcing continuous work exclusively through the generator.
Key Project Elements
| Element | Purpose |
herd/_model.goat | Main application definition: entities, properties, relationships, roles, and other model elements. |
herd/ | Automation, migration, testing, and generation scripts, as well as fixture data. |
herd/gen/templates/ | Templates defining how code is generated. |
scripts/ | CLI tools supporting generation, script execution, and the development process. |
| application source code | Manually maintained backend and frontend elements used by the generator. |
| main project directory | Final generated application, ready for further manual work. |
Application Model
File:
herd/_model.goatdescribes the structure of the application's domain.
It may contain, among other things:
- entities,
- fields,
- data types,
- relationships,
- roles,
- shared properties,
- information used by the backend and frontend.
The model should primarily describe the intent and structure of the application, rather than the implementation details of every file.
Based on it, Goat can generate consistent elements across multiple system layers, for example:
- models,
- DAOs and repositories,
- DTOs,
- API endpoints,
- CRUD commands,
- SQL migrations,
- forms,
- tables,
- administrative views,
- user interface elements.
The greatest benefit is not merely creating files, but the ability to maintain consistency between multiple representations of the same entity.
A model change can be reflected simultaneously in the backend, database, and frontend.
Code Generator
The generator combines several sources:
application model
+
templates
+
source code
↓
application codeThe result is a complete application located in the main project directory.
However, generated code is not treated as read-only code.
You can:
- edit it,
- extend it,
- refactor it,
- adapt it to specific requirements,
- supplement it with your own logic,
- commit it together with the remaining project changes.
Goat is intended to automate work where generation provides a real benefit. However, it does not require every subsequent change to be made exclusively through the model or templates.
Editing generated code
After generating an application, you receive regular source code over which you retain full control.
For example, the generator can create:
- an entity,
- a basic API,
- a DAO,
- a form,
- a list view,
- basic CRUD operations.
You can then manually add:
- additional business logic,
- non-standard validation,
- more complex queries,
- additional endpoints,
- custom actions,
- your own interface components,
- integrations with external systems.
Not every manual change needs to be moved into the generator.
If a modification is specific to one place in the application, it is often more cost-effective to change the code directly.
However, if the same change should apply in many similar places, it is worth moving it into the generator model or template.
A good criterion is the question:
Does this change describe the specifics of a particular feature, or a rule that should be repeated by the generator?
Regenerating the application
After changing the model, you can run:
goat reThe generator analyzes the current application definition and applies the resulting changes.
A typical workflow may look like this:
model change
↓
goat re
↓
code update
↓
manual refinement
↓
git diff
↓
testsAfter regeneration, it is always worth checking:
git diffThis lets you see exactly which parts of the project were changed by the generator and decide which changes should be included in the commit.
Git remains an important part of working with Goat: the generator proposes specific code changes, while the developer controls their final scope.
Model, template, or direct editing?
Goat does not impose a single way of making changes.
Depending on the nature of the task, you can work at different levels.
Changing the model
If the change concerns the domain structure, start with:
herd/_model.goatExamples:
- adding an entity,
- adding a field,
- changing a data type,
- changing a relationship,
- adding a role,
- changing a shared property.
In this case, the model is the best place because it allows the generator to maintain consistency between different layers of the application.
Changing the template
If you want to change how an entire class of similar elements is generated, the appropriate place may be:
herd/gen/templates/Examples:
- changing the structure of all endpoints,
- adding a shared annotation,
- changing how forms are generated,
- changing the default CRUD component,
- introducing a new convention in generated code.
The model then determines what should be created, while the template defines how the default implementation should look.
Direct code editing
If the change concerns a specific case and there is no point in extending the generator with it, you can simply modify the generated code.
Examples:
- a special business rule,
- a one-off exception,
- a custom endpoint,
- an additional integration,
- a specific frontend component.
This is a deliberate part of the Goat philosophy.
The generator should eliminate repetitive work, not force the project to adapt to the generator's limitations.
Application layers
main.go starts the application core.
The core is responsible, among other things, for:
- loading configuration,
- initializing dependencies,
- registering services,
- preparing application infrastructure,
- handling the command-line interface.
As a result, different operations use the same application environment.
Example commands:
serve
db:migrate
crud:doc:persistdo not need to be implemented as separate tools.
They can share:
- configuration,
- database access,
- services,
- logging,
- authorization mechanisms,
- the rest of the project infrastructure.
This facilitates both application development and the creation of administrative tools and automation processes.
Domain model and generated code
Entities defined in:
herd/_model.goatcan be used to generate elements of different application layers.
Example flow:
entity
↓
backend model
↓
DAO / repository
↓
DTO
↓
API
↓
form
↓
list viewThis reduces the need to describe the same data structure multiple times.
Instead of manually synchronizing several application layers, basic information can come from a shared definition.
This does not mean, however, that all layers must remain identical or be fully generated. Each generated element can be further adapted to the needs of the application.
.goat scripts
The herd/ directory also serves as the project's automation layer.
.goat scripts can describe entire development processes, including:
- starting the environment,
- generating the application,
- migrations,
- database initialization,
- loading fixture data,
- building the frontend,
- synchronizing translations,
- running tests.
For example:
goat run:script --path=herd/dev.goatcan run the complete workflow required to start working on the project.
As a result, the way the application is built, tested, and run remains part of the repository.
There is no need to maintain a separate set of manual instructions for each operating system or development workstation.
Script execution isolation
One of Goat's important assumptions is limiting the impact of executed scripts on the host system.
Tools used during building and testing can be run in an isolated Docker-based environment.
This includes, among others:
- Go,
- Node.js,
- npm,
- PostgreSQL,
- Playwright,
- build tools,
- additional dependencies required by the project.
This reduces dependence on the developer's local computer configuration and standardizes the environment across the team and CI.
Isolation also limits the scope of resources available to executed scripts.
In a typical case, they can work on the current project without unrestricted access to the host's other data.
This is particularly important when executing code from external dependencies, for example, installation scripts run by npm install.
Architecture and working with AI
Goat's architecture was designed to work well with AI tools as well.
In a traditional project, even a small change in requirements may involve analyzing many files:
model
DTO
DAO
API
migration
form
view
frontendIn a Goat-based project, some of these changes can be described at a much higher level:
requirement change
↓
model change
↓
goat re
↓
updating multiple layersAI can then work with a smaller context and focus on the intent of the change instead of analyzing the entire generated implementation.
This makes it possible to:
- limit the amount of code passed to the model,
- reduce the cost of analysis,
- shorten the time needed to understand the change,
- limit the number of modified files,
- reduce the risk of inconsistencies between layers.
At the same time, Goat does not require working exclusively at a high level.
If a change concerns one specific piece of code, AI or a developer can modify it directly.
This provides two complementary levels of work:
high-level — change the model, configuration, or template and regenerate,
low-level — directly edit the application code.
The choice of level depends on which approach is simpler, more readable, and less costly to maintain.
Typical workflow
Work on a model change may look as follows:
# change the application model
vim herd/_model.goat
# generate the resulting changes
goat re
# check the result
git diff
# refine the code manually if necessary
# run tests
goat run:script --path=herd/test.goat
# check the final scope of changes
git statusFor a purely business or implementation change, regeneration may not be necessary. You can modify the relevant piece of code directly and run the tests.
Most important principle
Goat does not try to own the generated code.
Its task is to generate repetitive elements faster and more consistently than manual implementation.
Once generated, the code belongs to the project.
You can modify, extend, and adapt it exactly as required by the application.
The generator should accelerate work, not lock the user into its generation model.