Cask doesn't just handle packaged GUI apps, but also binary distributions of command-line apps that are hard to compile from source; or are proprietary/closed-source; or are actually sandboxed point-release distributions of {software written in a scripting language + its vendored deps + its runtime}. None of these will have any sort of updater built into them, but they'll all be updated through `brew cask upgrade`. (Any random Golang binary you might download off of Github? Before I even Google it, I type `brew search $whatever` into my terminal. Way easier to keep updated than even the `go get`-installed version, and also doesn't require that you have go installed.)

As well, even for GUI software that has Sparkle-like update support built in, if you don't use it very often, it won't get updated until the next time you launch it, and even then will only get updated if you're online at the time you launch it. This leaves you vulnerable in the specific case that the software is offline but opening an untrusted document (e.g. one you originally downloaded from the network.) The vulnerability in the app will likely be exploited immediately upon launch of the app, before the app gets to update itself. Doing a periodic `brew cask upgrade` also takes care of these (at the annoying expense of constantly upgrading apps that put out a new release every few days. Looking at you, Etcher.)

Also, besides upgrading, brew is useful just for grabbing all this third-party software without having to run hither and thither to find it. Sometimes the only web route to downloading a piece of software involves one of those skeezy download-hosting sites that redirects you through several ad walls, or requires that you make an account to download. With brew, you skip all that — just e.g. `brew cask install megasync` or `brew cask install plex-media-server`.

Also, brew suppresses any interactive GUI elements of the install/update process, if it possibly can. `brew cask install java`, besides skipping navigating through ten pages of links to links to TOS agreements to links on Oracle's website, also does not summon forth the Java installer GUI, but rather does everything in the command-line. (As far as I can tell, it doesn't install the Java updater, either; brew takes over responsibility for upgrading Java, so there's no need for an independent update checker.)

And there's also `brew cask zap`, which is an uninstaller that actually cleans up after the program, removing not only the application itself, but also all the stuff the app either installed, or later created at runtime, under /Library. This takes advantage of Apple .pkg BOM manifests if it can; but also has a manually-specified section in brew formulae, to allow it to zap away the cruft of apps that don't bother to keep track of themselves (e.g. Zoom, zerotier, usboverdrive, switchresx, etc.)

There's also `brew services` — brew packages (and I think brew casks, too) can install launchd unit-file templates; and you can then enable/disable these (i.e. [un]link them in ~/Library/LaunchDaemons), and manage the lifecycle of (start/stop/status) these (i.e. call into launchctl), through `brew services`.

(My own `brew services` currently contains: docker-machine, ifs, postgresql, reds, ssh-askpass, unbound. I use it quite a lot! Much less arcane than launchctl itself.)

-----

Oh, and disregarding all that, and addressing the assumption underlying your arguments (that homebrew "messes with the filesystem"): you can keep a brew installation that doesn't mess with the filesystem at all (see https://docs.brew.sh/Installation#untar-anywhere).

It's just that doing so will confuse exactly the type of pre-compiled CLI (or POSIX-y GUI) tools that `brew cask` is meant to help you install. These would be the ones where important paths (e.g. $PREFIX/etc, $PREFIX/share) are burned into the compiled binary, and so by necessity one fixed prefix has to be selected to burn in at compile time. Usually the fixed prefix chosen is `/usr/local`.

People install Homebrew to /usr/local (or rather, allow Homebrew to link casks into /usr/local) because it's better than the alternative — where that alternative isn't "all that stuff gets installed in a clean way instead" but rather "all that stuff makes a mess of /usr/local on its own, but now there's nothing watching it do it, so your /usr/local is now a permanent, opaque mess rather than a set of managed overlays." (Ever install LaTeX on macOS from its upstream download? Or how about calibre? PostGIS? R? They all make exactly these kinds of messes in /usr/local. At least, by letting brew install them, you can now upgrade or remove them; and it'll notice if one package's files are trampling over another's.)