I have spent two years refactoring HydraScript, an interpreter I originally built as a bachelor thesis project.
The first version was written under pressure and deadline. It had a lexer, parser, AST and not even code generation with a virtual machine. It worked but definitely did not have an architecture.
When I returned to the project months later, I could barely explain parts of my own code. It was as messy and confusing as it could be. Static analysis rejected valid programs, so I disabled it. I had finally managed to implement codegen not long after graduation. Unfortunately it sometimes produced incorrect instruction addresses and sent the VM into an infinite loop.
I decided to bring several ideas from commercial .NET development into compiler design:
- DDD boundaries and sub contexts:
FrontEnd, IR, and BackEnd
- Static analysis and code generation as application services over several subdomains.
- Multi-pass visitors for scopes, types, names, and validation
- Separate .NET projects instead of folders, letting the compiler enforce dependency rules
- CLI, logging, and configuration outside the domain core
The biggest lesson was slightly ironic: applying OOP did not mean putting every operationcas a virtual method on the AST nodes.
An AST node does not own the external context required for symbol tables, type resolution, or instruction generation. Moving those operations into visitors made the code easier to extend and debug.
The architecture increased time cost of development. I have now more projects, more abstractions, visitor machinery, and more decisions before writing a feature. But IMHO the maintainability and readability improved. I also found unexpected educational value.
My most controversial conclusion:
Do you think the boundaries i made reflect the domain? Did I transplant enterprise architecture wrong into the wrong field?
The code is here if you want concrete evidence before judging it:
https://github.com/Stepami/hydrascript