The exact method depends on the language. In C/C++, you'd limit the include paths for each library. In C#, you'd have to compile different assemblies. And so on.
Eventually they tear down any boundary, even those in the build system.
Developer discipline is something that eludes many companies for lack of enough high quality engineers and awareness for the problem in upper management. It's easier to quibble over formatting guidelines.
If your developers are writing crap code in a monolith they're going to continue writing crap code in microservices but now you have new problems of deployment, observability, performance, debugging, etc etc.
As an aside I have a sneaking probably ahistorical suspicion microservices hype happened because people realised Ruby (or similar trendy dynamic languages) often ended up being a write only framework and rather than try and recover some codebase sanity people would rather abandon the code entirely and chase the new codebase high.
Anecdotally I witnessed this once. There was this huge ball of mud we had that worked okay-ish. Then the architects decided "hey microservices could solve this", so we started building out microservices that became a distributed ball of mud. Every microservice shared and passed a singular data model across ~30 microservices, which made things interesting when we needed to change that model. Also, we took mediocre developers and asked them to apply rigor they didn't have in developing these services to that they were "prepared" for the failures that happen with distributed systems.
The big upside to management though was that we could farm out parts of the system to different teams on different sides of the planet and have each of them build out the microservices, with each team having different standards as to what is acceptable (what response messages look like, what coding standards should be, ect). All of this was less of a technical problem and more of a managment one, but we felt the pain of it as it was made into a technical problem.
Of course, any dev could just remove the test, and by that tear down that boundary too. But that test being removed is much more obvious to the code reviewer who would not have noticed the dependency being snuck in. The architectural tests can also contain comments that form a high level documentation of the architecture. Unlike "architectural documents" in the form of some up front word document, this type tends to stay up to date.
In the same run one can also validate that there are no incompatible transitive dependencies.
Within assemblies (between namespaces) is much harder unfortunately. That means assemblies have to be made to make module boundaries obvious, even though it’s a bit of an antipattern. There are tools such as nsdepcop that watch namespace dependencies, but it’s brittle and a lot of work.
I suspect that this bandwidth problem exists elsewhere also.
Correct. That is why the advice not to start with Microservices. Perhaps later may make sense; but not in the beginning.
So, yet another comment saying that modularity requires a distributed system.
A monolith makes developing initially a lot easier. Over 15 years though, you are bound to have developers of various calibre leave their mark on it. Many don't even understand good modelling and inevitably drive an otherwise healthy monolith with well defined boundaries into a soup of couplings between domain concepts that should not know about each other. In theory though it is totally possible to have micro systems within the same monolith code if you model things that way.
Eventually, your team will flip the table and start thinking how to avoid the problems they are having with the monolith and decide to do down the micro-services way. In most situations developers are likely to face a lack of commitment from the business to spend time/money on a technical problem they do not understand but have to support the development of for a lengthy period of time. Most developers will compromise by building pseudo micro-services without their own databases which send requests to the monolith where the core business logic had to stay.
The benefit of micro-services IMO is driven from being able to separate business domains in a way that keeps each responsibility simple to understand for a new comer and compact enough to not hide a great deal of complexity. It's worth saying this is a very hard thing to achieve and the average developer shop won't have the experience to be able to pull it off.
This is all to say, regardless of Monolith or Micro-services architecture, the key is experience and discipline and without a good amount of both in your team the outcome is unlikely to be a success over a long enough period of time. This is the curse of a business that lives long enough.