Transparent scope and "referential hell" mean that you CANNOT KNOW what part of the system will be updated when you introduce a change using $scope.$apply().
You have no guarantees.
This is a design tradeoff.
Do you know that each time you call $scope.$apply() you actually call $rootScope.$apply()?
And this call updates all scopes and run all your watches?
Moreover, $rootScope.$apply() is called each time when:
- $timeout handler is invoked (almost all debounce services are broken by design)
- $http receives a response (yeah, if you have a polling implemented on $http ...)
- any DOM handler is called (have you throttled your ng-mouseovers? They actually invoke ALL your $watches, and built-in digest phasing does not really help)
If you know, that some change is localised (like, if you click the button, only the same $scope will be affected), then you MUST use $scope.$digest.
But again, you will face nasty "$digest is already in progress" issue...