The only "not so natural" things that may come from DX12 is that you need to avoid modifying resources when they are used by a command list. I do this by having dual command allocator and constant buffer that are swapped when a frame is finished.
I find this to be a really natural change to, as it's just the result of being honest about parallel programming :)
All the old APIs have tried really hard to pretend that your grqphics code is single threaded - that your function calls have an immediate result.
In truth all CPU+GPU programming is "multithreaded", so an honest API should reflect that.
When using the old APIs, which hide these details, it's very easy to do horribly slow operations, like accidentally read from write-combined memory regions or synchronize the CPU/GPU to lock a resource. To use these old APIs effectively, you really had to actually know what was happening behind their lies and work with the reality -- e.g. this means that in D3D11 you should already be taking care to avoid modifying a resource that is in use! Lot's of engines already use double buffering or ring buffering on D3D11, which is much more natural now on D3D12.
I do this by having dual command allocator and constant buffer that are swapped when a frame is finished.
You are supposed to use signals.
Double buffering works fine, as long as you can guarantee that the gpu has finished the previous frame before the CPU begins using that buffer... Which requires the use of a signal, yeah :)
We use the same strategy on D3D9/D3D11 for transient vertex data - CPU writes into unsynchronized buffers (NOOVERWRITE flag), swapping which one is used per frame (or which range of the buffer is used each frame). The CPU then just has to wait on an event signalling that the GPU has finished the previous frame.