It was designed for building large real-world applications and separates specification from implementation by using separately compilable specification and body files:
https://learn.adacore.com/courses/intro-to-ada/chapters/modu...
This allows the interface between modules to be defined concretely without relying on the implementations.
IMO, you always need what I call an "application meta-model"; a set of decisions/constraints you enforce to make developing a large monolith sane. Examples: integration between two features should be implemented in a separate module that depend on both features, such that neither feature depend on the other; exposing well-defined interfaces from modules for functionality that is expected by cross-cutting concerns, e.g. security/logging/caching/etc.
The advantage is that the Interface, Implementation and Initialization are all in one source file, and dependencies are resolved automatically. You don't need a separate build system.
Plus, strings just work. You don't have to allocate them, dispose of them, etc.
[1] https://www.pcorner.com/list/PASCAL/TASKER4.ZIP/TASKER.PAS/
i'm sort of cheating, but: if you add more context, the decision will likely be easier. what's the architecture for? what are the key constraints of your context that you need to optimise for? is modularity the primary constraint, or is it secondary or tertiary to other concerns? e.g. if the architecture will be used to build engines for fancy AAA games, where memory bandwidth is a major consideration, maybe your choices are limited to C/C++ so you can squeeze the most out of the hardware, and make use of existing middleware. if your monolithic architecture needs to be adopted by a company that has 1000s of developers who are familiar with java, then maybe to make it easier to sell internally, it needs to be implemented in java, so the switch cost is reduced for teams who you're trying to convince to adopt it. if you're building some saas thing, then you are probably going to end up using javascript for some of it. if the architecture will be used by teams with large numbers of recent grads / junior devs to deliver projects or ship features, maybe it's best if the language doesn't contain too many huge foot-guns.
Me experience: functional constructs and declarative problem solving helps in grand scheme, as does putting your time into building missing abstractions or using right library.
with big monolithic architecture not only modularity important. also good if language has good set of libraries. also easier to maintain with strong typing. no strong typing in large code base is deal breaker to me.
The Delphi component model - and the Visual Component Library (VCL) is such that:
a) It is trivial to create either visual or non-visual components for Delphi which can be installed at runtime into the IDE using its own registration process which is also trivial.
b) Once registered in the IDE, these components can be drag-dropped onto the forms and setup using "property sheets" which can also be heavily customised by the components. These property sheets can be simple screens or even very sophisticated screens with tabs etc. It is possible for components to even have run time property sheets.
c) The components can be installed as packs
d) These components can also be used at runtime without being registered into the IDE.
e) The components can be linked into the application staticly
Other than Delphi, I find the PHP's ability - especially in apps like Wordpress, to support a range of plugins that can be installed/uninstalled at runtime to be extremely powerful.
Smalltalk, Self, Common Lisp, Clojure
Or you could just be super specific about how you build things from day 1.
A recent app I worked on had no logic in models or controllers, but everything was in name-spaced active_jobs. Super easy to find what you're looking for and DRY was not something this team loved. Simple and contained was way more important.