|There's more than one way to do things|
Put (almost) everything in modules. This makes testing easy, but requires more exposure of subs than I want
I don't understand that point at all; maybe your definition of "exposure" is different than mine.
In Perl, every named subroutine can in principle be called from the outside, independently if it is defined in a script file or a module. (a script can do another script, and then access that second script's subs)
By convention, subs starting with an underscore are consider "private", so if you want to reduce exposure of a sub inside a module, simply mark it with an underscore.
as well as more linkages between modules
I don't see that as a problem either. You can write
Or you can write
And have just one more level of indirection, but an easier time to test all the other subroutines from the module without running doit.
Of course that approach isn't very different from writing all the code in the script, no code in the mainline, and at the end of the script putting a
That way you can also load the script as do 'yourscript.pl' without executing sub doit.