Implementations and analysis of my python login system (~80 lines) I wrote this login system in the evening and i would like to know what should I fix / change, if should I use classes and, about readability, if this code is OK.
I'm a begginer in coding so made this small project only for exercising, but good to know wether if i'm things doing right or wrong.
import os.path

credentials = []
database_path = &quot;C:\\Users\\filip\\PycharmProjects\\database&quot;
database = os.listdir(database_path)


def initialize():
    choose_option = int(input('0. Exit\n1. Login\n2. Sign up\n\nOption: '))
    if choose_option == 1:
        login_func()
    elif choose_option == 2:
        get_username(), get_password(), add_credentials_to_database()
    elif choose_option == 0:
        exit('Exiting...')
    else:
        print(&quot;You should enter '0', '1', or '2'.&quot;)
        initialize()
        

def get_username():
    username = input('Enter username: ')
    if f'{username}.txt' not in database:
        if len(username) &gt;= 8:
            credentials.append(username)
        else:
            print('Username too short (min 8 characters)\n\n')
            get_username()
    else:
        print('Username already exists')
        get_username()


def get_password():
    password = input('Enter password: ')
    if len(password) &gt;= 8:
        confirm_password = input('Confirm password: ')
        if password == confirm_password:
            credentials.append(password)
        else:
            print('Password does not match with confirmation\n\n')
            get_password()
    else:
        print('Password too short (min 8 characters)\n\n')
        get_password()
    

def add_credentials_to_database():
    credentials_file = os.path.join(database_path, f'{credentials[0]}.txt')
    file = open(credentials_file, 'w')
    file.write(credentials[1])
    file.close()
    print('Account created successfully\n\n')
    initialize()

def login_func():
    database = os.listdir(database_path) #Refreshes the database, so if you create an account you can login without having to run the program again
    enter_login = input('Enter username: ')
    if f'{enter_login}.txt' in database:
        for file in database:
            if f'{enter_login}.txt' == file:
                enter_password = input('Enter password: ')
                file = open(f&quot;C:\\Users\\filip\\PycharmProjects\\database\\{file}&quot;, 'r')
                if enter_password == file.read():
                    print('WELCOME!')
                else:
                    print('Invalid password\n\n')
                    login_func()
    else:
        print('Invalid username\n\n')
        login_func()


if __name__ == '__main__':
    initialize()

Also, if you remember, please tell me in the comments simple projects that you did when you were learning, so I can do it too.
ps: I know this login system is not secure at all but maybe later I could do some encryptation on those credencials...
When entering passwords, you may want to opt for using the getpass library. It allows you (if the console supports it) to hide passwords as they're being typed. You'd use getpass.getpass just as you are using input to ask for the password. The only difference is, the password won't show in the console as it's typed.

You're using recursion in get_password in order to ask the user again. This isn't a good idea. If the user fails too many times, your program can crash. Just use a loop:
def get_password():
    while True:
        password = input('Enter password: ')
        if len(password) &gt;= 8:
            confirm_password = input('Confirm password: ')
            if password == confirm_password:
                credentials.append(password)
                return
            else:
                print('Password does not match with confirmation\n\n')
        else:
            print('Password too short (min 8 characters)\n\n')

I'm looping until correct input is entered, then I'm just returning from within the infinite loop.
And the same goes for the rest of your functions. Recursion is great when used properly, but it carries more problems than most other looping methods. Use it in cases where it's appropriate (like when the problem itself is recursive), not just for general looping.

add_credentials would be safer if it used a with block to close the file for you:
def add_credentials_to_database():
    credentials_file = os.path.join(database_path, f'{credentials[0]}.txt')
    with open(credentials_file, 'w') as file:
        file.write(credentials[1])
    print('Account created successfully\n\n')
    initialize()

Now, even if an error were to happen half-way through the function, there's a greater chance that the file would be closed. Making a practice of using with also prevents you from forgetting to add a call to file.close().

At the top you have:
credentials = []
database_path = &quot;C:\\Users\\filip\\PycharmProjects\\database&quot;
database = os.listdir(database_path)

A few things to note:

credentials shouldn't be a global here. Global mutable states (which credentials is) tend to complicate code and make it harder to understand. In this set up, I would probably make credentials a local variable of initialize, have get_password and the like to return the data instead of adding it to the list directly, and manually pass that data to add_credentials_to_database.

database_path is a module-level constant, and as such should be in upper-case like DATABASE_PATH according to PEP8.

Arguably database is also (effectively) constant, so it should also be in upper-case.



In initialize, I would get rid of the int conversion of choose_option. All the call to int is doing is allowing for a ValueError to be raised if the user enters in a dumb menu option like &quot;a&quot;. Just use the raw string value and compare against it:
choose_option = input('0. Exit\n1. Login\n2. Sign up\n\nOption: ')
if choose_option == &quot;1&quot;:
    . . .

If you actually needed it as an integer to do math on it or something, it would be different. It's simply an &quot;indicator value&quot; though, so the type of choose_option doesn't really matter, and keeping it as a string has less problems.


And my favorite projects that I still do whenever I learn a new language are:

A naïve, bruteforce prime number finder (and then maybe a sieve). Fairly basic.
Conway's Game of Life. Harder, and has the potential to turn into a large project.
A Mandelbrot Set image generator. Even harder, and usually becomes a larger project.