In terms of simplicity, there isn't much here that is overly complicated. I would get rid of those [bng]s in [play], though, as they are completely unnecessary. Also, just connect the [inlet~] in [record] directly to [writesf~]. [s~] and [r~] can result in a blocksize delay unless their order is explicitly enforced using subpatches. In this case, it would be better to just connect them to avoid that delay.
There are improvements that can be made in terms of flexibility and conforming to common Pd practices. The most glaring issue I have with this is the and [r lengthvariable]. It's generally a bad idea to have two abstractions communicating with and [r ], especially without some dollar variable as a kind of ID saying they're supposed to be paired up. In this case, you can only call one instance of each because other instances will end up cross-communicating. And if there's only one instance, it may as well just be a subpatch instead.
One way you could fix that is to send the length via an inlet from the parent patch. Then you could just patch it to both and they'll be on the same page. You could even make it so that [record] accepts a length instead of a bang to start recording, similar to how [delay] will take a time in ms that will both change the delay time and trigger a bang. I generally think it's good practice to make your abstractions accept messages that override creation arguments, anyway, as it makes them more flexible.
A few other things:
* I think [record] should output a bang instead of a [stop( message. "Bang when done" is common among a lot of externals, anyway, and you may find a time where you want to trigger more than just [play] when the recording stops.
* I really have no idea why "length" is being supplied as an argument. I'm guessing you're using it as a reminder, but that's really what helpfiles are for. If you want to use a dummy argument, save it for the end. I've never seen an external or abstraction that uses a dummy argument before the real ones.
* This isn't a huge deal, but with audio abstractions, it's common practice to put the audio [inlet~]s on the left and message [inlet]s on the right. I know you're probably thinking that it's a hot inlet and should be on the left, but really it should be the left of just the message inlets. I personally like to just make one [inlet] for audio abstractions when practical that takes all hot and cold messages and use [route] to send them where they need to go.
* You might want to stick a [switch~] in them to turn processing off when it's not doing anything. I mean, there's only a couple of dsp objects in there, so it's not a huge deal. But wasted processing is wasted processing, and if you have a bunch of instances, it can still add up.