Question: According to the docs, Pydantic "ORM mode" (enabled with orm_mode = True in Config) is needed to enable the from_orm method in order to create a model instance by reading attributes from another class instance. If ORM mode is not enabled, the from_orm method raises an exception. My questions are:  Are there any other effects (in functionality, performance, etc.) in enabling ORM mode? If not, why is it an opt-in feature? 

Answer: Fortunately, at least the first question can be answered fairly easily. As of version 1.10.4, there are only two places (aside from the plugins), where orm_mode comes into play.  BaseModel.from_orm This is basically an alternative constructor. It forgoes the regular __init__ method in favor of a marginally different setup. Not sure, why that is designed this way. But the orm_mode flag must be set for this method to not raise an error. Straightforward. I see no hidden surprises here.  BaseModel.validate This method is the default validator for the BaseModel type. Without the orm_mode flag, the validator expects a value that is either 1) an instance of that particular model, 2) a dictionary that can be unpacked into the constructor of that model, or 3) something that can be coerced to a dictionary, then to be unpacked into the constructor of that model. If orm_mode is True and the validator encounters something that is not an instance of the model and not a dictionary, it assumes it is an object that can be passed to the aforementioned from_orm method and calls that instead of trying the dict coercion. Note that this method is not called during initialization and it is not called if something is assigned to a model field of any type that isn't BaseModel. It only comes into play, when you are dealing with nested models (and the objects that serve as the data input are also nested), i.e. with a model that has a field annotated with another model. Only then will the outer model call the validate method of the inner model. Consider the following: from __future__ import annotations from typing import TypeVar from pydantic import BaseModel M = TypeVar("M", bound=BaseModel) class Foo(BaseModel): x: int @classmethod def validate(cls: type[M], value: object) -> M: print("called `Foo.validate`") return super().validate(value) class Config: orm_mode = True class A: x = 1 foo = Foo.from_orm(A) print(foo.json())  The output is {"x": 1} and we see that Foo.validate was not called. Now we extend this a bit: ... class Bar(BaseModel): f: Foo class Config: orm_mode = True class B: f = A bar = Bar.from_orm(B) print(bar.json())  The new output: called `Foo.validate` {"f": {"x": 1}}  Now the validator was called as expected and if we were to inject a similar print statement into Foo.from_orm we would see that it too was called, when we called Bar.from_orm right after Foo.validate was called. This may be relevant in certain niche situations, but generally speaking I would argue that this cascading application of from_orm during validation makes sense and should accommodate the main intended use case -- database ORM objects. If you want different behavior during validation, you can always define your own validator methods or even simply override the validate method (depending on your use case).  There are no other uses of orm_mode in the source code, so that is it in terms of functionality. Performance is not really relevant in those contexts IMO because it is just an altogether different way of initializing an instance of the model. Unless you are interested in whether or not it is faster to first manually turn your ORM object into a dictionary and pass that to parse_obj or to just call from_orm on it. You could benchmark that fairly easily though. No other functionality of the BaseModel is affected (performance wise) by that config setting in any way that I can see.  To your second question, I could only speculate. So I will refrain from answering. There is an issue already open for a while that suggests removing the setting altogether, which seems to be kind of in line with your reasoning that it should not be "opt-in" in any case. I am not sure if Samuel Colvin is still accepting backwards-incompatible feature requests for v2, but this issue has not gotten a lot of attention. You might want to participate there.