Going along with some of the above, there are many options, not just those two.
My question is how should I handle crashes in my engine code?
You fix the issue. If the code is crashing it is a critically serious A-level red-flag bug.
How you get around to fixing the issue is up to you.
Game engines should never crash when used as designed. If the person violates the design, perhaps passing invalid pointers or garbage values, the code's response should vary.
Leverage the fact that debug builds, testing builds, and final builds are different and can contain different error handlers.
DURING DEVELOPMENT
If this engine crash is during development you want it to fail noisily and provide all the information needed to the development team. This typically means a crash dump and a big ugly message. Write crash handlers for all the ways the program can die and make them create minidumps, and make it very easy for the person to get that minidump to the developer.
In most of the professional world, a crash bug is a critical bug that prevents shipping. If your game is being released on a game console then any crash will automatically fail the game certification process. When discovered this way the cost to a studio for a single crash is thousands of dollars. It costs time and salary to fix the bug, time and money to go back through certification, and possibly money if shipping dates are missed or in jeopardy. Of course, a console game crashing in the wild can end up destroying the entire product, so don't do that.
Note that an engine non-crash condition doesn't deserve that same response. During development you can provide big feedback to the developer that they are doing it wrong, abort the action, then attempt to continue. It may be a big warning "Error parsing data file <whatever.json> line 123", or "Code is using graphics functions after D3D11 surface was already reported as broken. FIX IT! Ignoring graphics call." During development you want these conditions to be called out visibly.
DURING MAINTENANCE
If the engine crash is during a product's maintenance cycle then you want it reported with all information needed to fix the bug. For game clients this typically means a crash dump and auto-uploading so you can correct and fix it. For servers this often means a bunch of emails auto-generated to the dev team with the dump attached or referenced.
For non-crash conditions during maintenance your engine should attempt to fail gracefully. This means you start logging the issues and provide a way to upload them. This is commonly done by games that have a checkbox like: "Report anonymous usage statistics to make the game better" If you don't run a server or can't run such a server in practice, this can be done with an exit handler that politely says "Problems were detected during gameplay, please email this log file to the developers" or something similar.
After you're done with product maintenance it doesn't really matter what you do.
Lets say D3D11 fails to initialize how should I handle that?
That is a known failure path, and it can happen for an enormous number of reasons. That should never be a crash by itself.
If your program is crashing due to this known and expected condition, that crash is a serious critical A-level red-flag bug. Identify your bug and fix it.
If someone else's code is crashing due to this known and expected condition, if your engine has the ability to detect and stop the behavior it should do so as described above. During development that means hard-to-ignore messages that get the defect fixed, and after development it means providing methods for the program to be patched.
For known failure paths generally a return value is sufficient. That is how DirectX itself handles it, and how most other major libraries handle similar events.