Blame bad interfaces, not abstractions
I've read a couple of posts from places like Hacker News in the past couple days in which their authors relate their hate of abstractions. They talk about being "too far from the metal" or unable to accomplish certain things. These can be valid complaints, but I think it's too easy to just paint all abstractions with the same brush.
Here's an abstraction: I ask you to stop the car. You do so. I trust that you will do it safely and in a way that doesn't launch everyone into their seat belts. Ideally, you'll do it "chauffeur's stop" style, where you ease off the pedal as the car slows so it doesn't jolt right at the end.
This kind of behavior doesn't come naturally, and it doesn't stick around, either. Take a ride with your average 15 year old with a learner's permit or your 85 year old grandfather and see how smoothly they stop. Odds are, it'll get the job done, but it'll slosh your soda.
Right there, we have an abstraction. It can be used to provide a luxurious and silky experience, or it can give you a full-on power slide where you think you're going to die. Since it can go either way, you can't say that merely having this abstraction yields any given result.
What if I micromanaged the driver instead? I could try to call out all of the stuff which has to happen. "See if there's anyone behind us following too closely for conditions that would make it unsafe to stop now. Move your foot to the brake pedal. Apply downward force. More. More. Less. LESS! Now hold it right there at the top".
That's nuts. You lack the sensory input to make all of those decisions, since it's not your foot on the pedal and you can't see in the rear-view mirror. There's also significant latency between you deciding a request is necessary and it actually having some effect on the car. You have to say something, then the driver has to parse it, consider it, and then take action.
Likewise, when it's time to build a daemon which runs on a machine which installs kernels, there are decisions to be made. Do you just expose a single interface which takes a URL to the bzImage and does all of the work? It has to fetch that file to a temp directory, then install the RPM and make sure LILO is happy with it. Do you want all of them to happen every time in a single request, or do you really need separate interfaces for each action?
When I wrote code to do this not too long ago, I opted for the former. Give it a URL and it does all of the installation work. It then either returns success or failure. There's nothing the requester can do if any of the constituent parts fail, so there was no reason to bother calling them separately.
Better still, by only having it be a single request to the target machine, it made my state machine that much smaller. Having three separate calls which must happen in sequence would have been possible, but it would have been annoying to track.
Finally, there's the matter of changing requirements. Maybe some day, the systems will stop using RPM and instead switch to another package scheme. If you've exposed "install an RPM" to clients, now you either have to add another interface and adjust all of your clients, or you have to kludge it into the "RPM" installer and then try to suppress the bile.
What if the process of installing kernels changes? Maybe you switch from LILO to GRUB, where merely editing the list is sufficient to make it start a kernel. You don't have to re-run a program any more. It also turns out that your kernel packages have a post-install script to add themselves to the GRUB list. Now what? Do you edit your client code to skip that step, or do you just keep it there and make it a NOP?
This kind of interface decision is best influenced by years of experience. If you haven't been around long enough to be bitten by one extreme or another, then you may not be adequately calibrated yet. At that point you either ask someone for advice, or if operating alone, guess and accept the fact you might have to rework it later.
It's okay. Your time will come.