A viewpoint we often miss when focussed on our own app, is that our app may well be installed in an org that has many other apps installed and lots of local customisation.
The SObjects that apps tend to share - such as
Contact - are particularly likely to have had fields and layouts and record types added. So automating the process of merging the changes we want into what is already there in an exact way is impossible: the software can't know the intent behind the existing configuration.
The SObjects that our app defines are potentially a simpler case and so a better case for automation. Though for the domain I work in customers want to add fields and change layouts there too.
The metadata API lets you write tooling (including in Apex) to do the updates. But that tooling would be complex as it would need to be interactive to deal with conflicts and user choices. If you and your app are new to Salesforce, your effort is probably better directed towards app features than this sort of tooling. (There may be 3rd party tools already written that could be leveraged.)
For our apps, we provide "deployment tasks": instructions to Salesforce administrators on what to do manually after the app is installed or upgraded in the form of documentation. Far from ideal, but it is a pragmatic solution.