Thank you @Jeffrey Drumm 

I've made several tests with User option and actually, even without this parameter, IRIS is started under the account provided during the installation :

guilbaud    1776       1  0 13:26 ?        00:00:00 /usr/irissys/bin/irisdb -s/usr/irissys/mgr/ -w/usr/irissys/mgr/ -cc -B -C/usr/irissys/iris.cpf*IRIS
guilbaud    1885    1776  0 13:26 ?        00:00:00 /usr/irissys/bin/irisdb WD                                                                         
guilbaud    1886    1776  0 13:26 ?        00:00:00 /usr/irissys/bin/irisdb GC                                                                         
guilbaud    1887    1776  0 13:26 ?        00:00:00 /usr/irissys/bin/irisdb JD                                                                         
guilbaud    1888    1776  0 13:26 ?        00:00:00 /usr/irissys/bin/irisdb AUX1                                                                       
guilbaud    1889    1776  0 13:26 ?        00:00:00 /usr/irissys/bin/irisdb AUX3                                                                       
guilbaud    1890    1776  0 13:26 ?        00:00:00 /usr/irissys/bin/irisdb AUX2                                                                       
guilbaud    1891    1776  0 13:26 ?        00:00:00 /usr/irissys/bin/irisdb AUX5                                                                       
guilbaud    1892    1776  0 13:26 ?        00:00:00 /usr/irissys/bin/irisdb AUX4                                                                       
guilbaud    1893    1776  0 13:26 ?        00:00:00 /usr/irissys/bin/irisdb AUX6                                                                       
guilbaud    1894    1776  0 13:26 ?        00:00:00 /usr/irissys/bin/irisdb AUX7                                                                       
guilbaud    1895    1776  0 13:26 ?        00:00:00 /usr/irissys/bin/irisdb DBXD                                                                       
irisusr     2033       1  0 13:26 ?        00:00:00 /usr/irissys/bin/irisdb -s/usr/irissys/mgr -cj -p13 START^MONITOR
irisusr     2041       1  0 13:26 ?        00:00:00 /usr/irissys/bin/irisdb -s/usr/irissys/mgr -cj -p13 START^CLNDMN
irisusr     2106       1  0 13:26 ?        00:00:00 /usr/irissys/bin/irisdb -s/usr/irissys/mgr -cj -p13 START^LMFMON
irisusr     2133       1  0 13:26 ?        00:00:00 /usr/irissys/bin/irisdb -s/usr/irissys/mgr -cj -p13 ^RECEIVE
irisusr     2146       1  0 13:26 ?        00:00:00 /usr/irissys/bin/irisdb -s/usr/irissys/mgr -cj -p19 Master^%SYS.SERVER
irisusr     2153       1  0 13:26 ?        00:00:00 /usr/irissys/bin/irisdb -s/usr/irissys/mgr -cj -p19 RunManager^%SYS.Task
irisusr     2157       1  0 13:26 ?        00:00:00 /usr/irissys/bin/irisdb -s/usr/irissys/mgr -cj -p19 Start^%SYS.Monitor.Control
irisusr     2178       1  0 13:26 ?        00:00:00 /usr/irissys/bin/irisdb -s/usr/irissys/mgr -cj -p19 RunDaemon^%SYS.WorkQueueMgr
irisusr     2191       1  0 13:26 ?        00:00:00 /usr/irissys/bin/irisdb -s/usr/irissys/mgr -cj -p19 RunRemoteQueueDaemon^%SYS.WorkQueueMgr
irisusr     2205    2178  0 13:26 ?        00:00:00 /usr/irissys/bin/irisdb -s/usr/irissys/mgr -cj -p24 startWork^%SYS.WorkQueueMgr
irisusr     2213    2178  0 13:26 ?        00:00:00 /usr/irissys/bin/irisdb -s/usr/irissys/mgr -cj -p24 startWork^%SYS.WorkQueueMgr
irisusr     2420    2178  0 13:26 ?        00:00:00 /usr/irissys/bin/irisdb -s/usr/irissys/mgr -cj -p24 startWork^%SYS.WorkQueueMgr

You can also use classes instead of routines.

For instance :

Class test.a Extends %RegisteredObject
{

ClassMethod run() As %Status
{
    Set sc = $$$OK
    read !,"Enter the hour: ",hr  
    read !,"Enter the minute: ",min 
    read !,"Enter the second: ",sec     
    do ..getInternalTime(hr,min,sec)

     /// Display some other internal time values :
    for a = 0,3600,43200,45296,86399 {
        w !,"Equivalent M time: ",a," (Time:",$zt(a),")",!
    }
    Return sc
}

ClassMethod getInternalTime(hr As %Integer, min As %Integer, sec As %Integer) As %Status
{
    set sc=$$$OK
    set Mtime=$$ConvertToMTime(hr,min,sec)
    write !,"Equivalent M time: ",Mtime," (Time:",$zt(Mtime),")",!
    return sc
}

ClassMethod ConvertToMTime(h, m, s) As %Integer
{
        Return (h*3600)+(m*60)+(s)
}

}

Will give : 

USER>w ##class(test.a).run()

Enter the hour: 12
Enter the minute: 34
Enter the second: 45
Equivalent M time: 45285 (Time:12:34:45)

Equivalent M time: 0 (Time:00:00:00)

Equivalent M time: 3600 (Time:01:00:00)

Equivalent M time: 43200 (Time:12:00:00)

Equivalent M time: 45296 (Time:12:34:56)

Equivalent M time: 86399 (Time:23:59:59)
1

Hi @James Rutledge 

welcome in our world !

Using this ROUTINE below :

ROUTINE test.1
    r !,"Enter the hour: ",hr  
    r !,"Enter the minute: ",min 
    r !,"Enter the second: ",sec 
    s Mtime=$$ConvertToMTime(hr,min,sec)
    w !,"Equivalent M time: ",Mtime," (Time:",$zt(Mtime),")",!

    /// Display some other internal time values :
    for a = 0,3600,43200,45296,86399 {
        w !,"Equivalent M time: ",a," (Time:",$zt(a),")",!
    }
    q
ConvertToMTime(h,m,s)
    q (h*3600)+(m*60)+(s)

Will display the following result :

USER>d ^test.1

Enter the hour: 12
Enter the minute: 34
Enter the second: 45
Equivalent M time: 45285 (Time:12:34:45)

Equivalent M time: 0 (Time:00:00:00)

Equivalent M time: 3600 (Time:01:00:00)

Equivalent M time: 43200 (Time:12:00:00)

Equivalent M time: 45296 (Time:12:34:56)

Equivalent M time: 86399 (Time:23:59:59)

Simply by using the Resource option in a CreateDatabase instruction of [Actions] section in a merge.cpf file :

[Actions]
CreateResource:Name=%DB_IRISAPP_DATA,Description="IRISAPP_DATA database"
CreateDatabase:Name=IRISAPP_DATA,Directory=/usr/irissys/mgr/IRISAPP_DATA,Resource=%DB_IRISAPP_DATA
CreateResource:Name=%DB_IRISAPP_CODE,Description="IRISAPP_CODE database"
CreateDatabase:Name=IRISAPP_CODE,Directory=/usr/irissys/mgr/IRISAPP_CODE,Resource=%DB_IRISAPP_CODE
CreateNamespace:Name=IRISAPP,Globals=IRISAPP_DATA,Routines=IRISAPP_CODE,Interop=1

Hi @Otto Medin 
very good point.

In order to get links visible on the production configuration, you can add this method :

/// Return an array of connections for drawing lines on the config diagram
ClassMethod OnGetConnections(Output pArray As %String, pItem As Ens.Config.Item)
{
	Do ##super(.pArray,pItem)
	If pItem.GetModifiedSetting("TargetConfigNames",.tValue) {
		For i=1:1:$L(tValue,",") { Set tOne=$ZStrip($P(tValue,",",i),"<>W")  Continue:""=tOne  Set pArray(tOne)="" }
	}
}

With the following results :

python account.py 
Deposit succeeded: Balance after deposit: 100
Deposit of negative amount failed as expected: Deposit amount must be non-negative
Withdrawal succeeded: Balance after withdrawal: 50
Withdrawal of more than available balance failed as expected: Insufficient funds
Balance invariant holds true
Balance invariant violated as expected: Balance invariant violated: Balance is negative
Account operations completed successfully

In Python :
 

class Account:
    def __init__(self):
        self.balance = 0

    def deposit(self, amount):
        if amount < 0:
            raise ValueError("Deposit amount must be non-negative")
        
        old_balance = self.balance
        self.balance += amount
        
        # Postconditions
        if self.balance != old_balance + amount:
            raise ValueError("Postcondition failed: Balance calculation error")

    def withdraw(self, amount):
        if amount < 0:
            raise ValueError("Withdrawal amount must be non-negative")
        if self.balance < amount:
            raise ValueError("Insufficient funds")

        old_balance = self.balance
        self.balance -= amount
        
        # Postconditions
        if self.balance != old_balance - amount:
            raise ValueError("Postcondition failed: Balance calculation error")

    def check_balance_invariant(self):
        if self.balance < 0:
            raise ValueError("Balance invariant violated: Balance is negative")

    @classmethod
    def test_account(cls):
        account = cls()
        
        try:
            # Test depositing a positive amount
            account.deposit(100)
            print("Deposit succeeded: Balance after deposit:", account.balance)
            
            # Test depositing a negative amount (should fail)
            account.deposit(-50)
        except ValueError as e:
            print("Deposit of negative amount failed as expected:", e)
        else:
            raise ValueError("Deposit of negative amount unexpectedly succeeded")
        
        try:
            # Test withdrawing a valid amount
            account.withdraw(50)
            print("Withdrawal succeeded: Balance after withdrawal:", account.balance)
            
            # Test withdrawing more than the available balance (should fail)
            account.withdraw(200)
        except ValueError as e:
            print("Withdrawal of more than available balance failed as expected:", e)
        else:
            raise ValueError("Withdrawal of more than available balance unexpectedly succeeded")
        
        try:
            # Check balance invariant (should succeed)
            account.check_balance_invariant()
            print("Balance invariant holds true")
        except ValueError as e:
            print("Balance invariant violated:", e)
        
        # Intentionally set balance to negative value to trigger balance invariant failure
        account.balance = -10
        
        try:
            # Check balance invariant (should fail)
            account.check_balance_invariant()
        except ValueError as e:
            print("Balance invariant violated as expected:", e)
        else:
            raise ValueError("Balance invariant unexpectedly held true")
        
        print("Account operations completed successfully")

# Run the test
Account.test_account()

With the following results :

write ##class(MyApp.Account).TestAccount()
Deposit succeeded: Balance after deposit: 100
Deposit of negative amount failed as expected: ERROR #5001: Deposit amount must be non-negative
Withdrawal succeeded: Balance after withdrawal: 50
Withdrawal of more than available balance failed as expected: ERROR #5001: Insufficient funds
Balance invariant holds true
Balance invariant violated as expected: ERROR #5001: Balance invariant violated: Balance is negative
Account operations completed successfully
1

Very good point @Herman Slagman 
To illustrate your point, in ObjectScript :

Include %occErrors

Class MyApp.Account Extends %Persistent
{

Property Balance As %Numeric;

/// Deposit money into the account
Method Deposit(amount As %Numeric) As %Status
{
    // Preconditions
    If amount < 0 {
        Return $$$ERROR($$$GeneralError, "Deposit amount must be non-negative")
    }

    // Store the old balance
    Set oldBalance = ..Balance

    // Update balance
    Set ..Balance = oldBalance + amount

    // Postconditions
    If (..Balance '= $$$NULLOREF) && (..Balance '= (oldBalance + amount)) {
        Return $$$ERROR($$$GeneralError, "Postcondition failed: Balance calculation error")
    }

    Quit $$$OK
}

/// Withdraw money from the account
Method Withdraw(amount As %Numeric) As %Status
{
    // Preconditions
    If amount < 0 {
        Return $$$ERROR($$$GeneralError, "Withdrawal amount must be non-negative")
    }
    If (..Balance = $$$NULLOREF) || (..Balance < amount) {
        Return $$$ERROR($$$GeneralError, "Insufficient funds")
    }

    // Store the old balance
    Set oldBalance = ..Balance

    // Update balance
    Set ..Balance = oldBalance - amount

    // Postconditions
    If (..Balance '= $$$NULLOREF) && (..Balance '= (oldBalance - amount)) {
        Return $$$ERROR($$$GeneralError, "Postcondition failed: Balance calculation error")
    }

    Quit $$$OK
}

/// Invariant: Balance should always be non-negative
Method CheckBalanceInvariant() As %Status
{
        Set tSC = $$$OK
        If ..Balance < 0 {
            Set tSC = $$$ERROR($$$GeneralError, "Balance invariant violated: Balance is negative")
        }
        Quit tSC
}

/// Class method to test the Account class
ClassMethod TestAccount() As %Status
{
    // Create a new instance of Account
    Set account = ##class(MyApp.Account).%New()
    
    // Initialize the balance
    Set account.Balance = 0
    
    // Test depositing a positive amount
    Set tSC = account.Deposit(100)
    If $$$ISERR(tSC) {
        Write "Deposit failed: ", $system.Status.GetErrorText(tSC), !
        Quit tSC
    }
    Write "Deposit succeeded: Balance after deposit: ", account.Balance, !
    
    // Test depositing a negative amount (should fail)
    Set tSC = account.Deposit(-50)
    If $$$ISERR(tSC) {
        Write "Deposit of negative amount failed as expected: ", $system.Status.GetErrorText(tSC), !
    } Else {
        Write "Deposit of negative amount unexpectedly succeeded", !
        Quit $$$ERROR($$$GeneralError, "Deposit of negative amount unexpectedly succeeded")
    }
    
    // Test withdrawing a valid amount
    Set tSC = account.Withdraw(50)
    If $$$ISERR(tSC) {
        Write "Withdrawal failed: ", $system.Status.GetErrorText(tSC), !
        Quit tSC
    }
    Write "Withdrawal succeeded: Balance after withdrawal: ", account.Balance, !
    
    // Test withdrawing more than the available balance (should fail)
    Set tSC = account.Withdraw(200)
    If $$$ISERR(tSC) {
        Write "Withdrawal of more than available balance failed as expected: ", $system.Status.GetErrorText(tSC), !
    } Else {
        Write "Withdrawal of more than available balance unexpectedly succeeded", !
        Quit $$$ERROR($$$GeneralError, "Withdrawal of more than available balance unexpectedly succeeded")
    }
    
    // Check balance invariant (should succeed)
    Set tSC = account.CheckBalanceInvariant()
    If $$$ISERR(tSC) {
        Write "Balance invariant violated: ", $system.Status.GetErrorText(tSC), !
        Quit tSC
    }
    Write "Balance invariant holds true", !
    
    // Intentionally set balance to negative value to trigger balance invariant failure
    Set account.Balance = -10
    
    // Check balance invariant (should fail)
    Set tSC = account.CheckBalanceInvariant()
    If $$$ISERR(tSC) {
        Write "Balance invariant violated as expected: ", $system.Status.GetErrorText(tSC), !
    } Else {
        Write "Balance invariant unexpectedly held true", !
        Quit $$$ERROR($$$GeneralError, "Balance invariant unexpectedly held true")
    }
    
    Write "Account operations completed successfully", !
    Quit $$$OK
}

Storage Default
{
<Data name="AccountDefaultData">
<Value name="1">
<Value>%%CLASSNAME</Value>
</Value>
<Value name="2">
<Value>Balance</Value>
</Value>
</Data>
<DataLocation>^MyApp.AccountD</DataLocation>
<DefaultData>AccountDefaultData</DefaultData>
<IdLocation>^MyApp.AccountD</IdLocation>
<IndexLocation>^MyApp.AccountI</IndexLocation>
<StreamLocation>^MyApp.AccountS</StreamLocation>
<Type>%Storage.Persistent</Type>
}

}

Many thanks @Alex Woodhead for your article.

For those who want to start with Alex's example, you can use the code below or online (process + production)

/// 
Class python.process.demo Extends Ens.BusinessProcessBPL
{

/// BPL Definition
XData BPL [ XMLNamespace = "http://www.intersystems.com/bpl" ]
{
<process language='python' request='Ens.StringRequest' response='Ens.StringResponse' height='2000' width='2000' >
<pyFromImport>
import requests
</pyFromImport>
<sequence xend='200' yend='1050' >
<switch name='What Drink' xpos='200' ypos='250' xend='200' yend='500' >
<annotation><![CDATA[looking for Drink keyword in request]]></annotation>
<case condition='request.StringValue.lower().find("coffee")&gt;0' name='is coffee' languageOverride='python' >
<assign name="Coffe" property="response.StringValue" value="&quot;Coffee&quot;" action="set" languageOverride="objectscript" xpos='200' ypos='400' />
</case>
<case condition='request.StringValue.lower().find("tea")&gt;0' name='is tea' languageOverride='python' >
<assign name="Tea" property="response.StringValue" value="&quot;Tea&quot;" action="set" languageOverride="objectscript" xpos='470' ypos='400' />
</case>
<default>
<assign name="Chocolate" property="response.StringValue" value="&quot;Chocolate&quot;" action="set" languageOverride="objectscript" xpos='740' ypos='400' />
</default>
</switch>
<if name='Is White' condition='request.StringValue.lower().find("black")==-1' languageOverride="python" xpos='200' ypos='600' xend='200' yend='850' >
<annotation><![CDATA[looking for absence of keyword "black" in request]]></annotation>
<true>
<assign name="Add Milk" property="response.StringValue" value="response.StringValue + &quot; MILK&quot;" action="set" languageOverride="python" xpos='335' ypos='750' />
</true>
</if>
<assign name="Add Hot Water" property="response.StringValue" value="response.StringValue + &quot; HOT WATER&quot;" action="set" languageOverride="python" xpos='200' ypos='950' />
</sequence>
</process>
}

Storage Default
{
<Type>%Storage.Persistent</Type>
}

}

I got an interesting answer from chat.fhir.org

Lloyd McKenzie says : 

R5 has a bunch of fixes and enhancements and is 'closer' to what the normative/locked down versions of each of the resources is likely to be. So, all things being equal, R5 is a better bet. However, the amount of implementation support for R5 is considerably less than for R4 and some jurisdictions may opt to not support R5 at all. (E.g. the U.S.) Therefore consider who you're going to need to share with.

The migration effort really depends on what you're implementing. Some resources (e.g. Patient, Observation, etc.) have changed very little - and the changes that did occur may not impact your application because you don't use those elements. Other resources have changed more radically. The amount of effort involved also depends on how your software is architected. You can see lists of changes and maps between R4 and R5 in the R5 spec on each resource page.

Finally, 'repository' is not the same as 'interface'. It's perfectly possible to expose both an R4 and an R5 API on the same internal data store. Typically you'll have to map between your internal storage representation and your FHIR API regardless of what version you support, so two interfaces just means two sets of maps.