A very useful exercise prior to writing any code is to use a lightweight version of Use Case Methodology.
The basic idea—and you don't have to sweat the specifics*—is to write down what it will ok like to the user to operate the system. This means all classes of users (e.g.: customers [ordinary users], administrators, security personnel(?), *c.).
These narratives should explain the desired functions of the system and use organic naming, that is, names actually used by the people using the system. For example, don't call "doors", "egress points" or "unlocked". "desecured" or any other artificial nonsense.
Naming of functions and variables plays a critical role in proper modularization of your code. A properly named subroutine/function will tell you what should and should not be included. Same with variables—proper naming leads so natural normalization, that is, using the variable to contain only one piece of data not requiring later manipulation to extract what is needed.
In your case a speculative set of names might be:
Enroll (for adding a user)
Remove (for removing any object e.g.: user, token, door)
Door (a door)
Reader (a reader)
Schedule (an access schedule)
&c
Once you understand the objects (this does not require OOP, though that can be helpful) involved and they have names that most closely represent their encapsulated reality, you can move on to defining functions, again, based on this same idea. Real world analogies are very helpful. Overly abstract—or literal—functions will lead to confusion when new functionality is requested while well chosen abstractions will ofter already support that functionality requiring little or no change to make it available.
Naming is more important that a neophyte programmer is likely to grasp, but my most successful projects have also tracked my most success design efforts—especially naming.
The basic idea—and you don't have to sweat the specifics*—is to write down what it will ok like to the user to operate the system. This means all classes of users (e.g.: customers [ordinary users], administrators, security personnel(?), *c.).
These narratives should explain the desired functions of the system and use organic naming, that is, names actually used by the people using the system. For example, don't call "doors", "egress points" or "unlocked". "desecured" or any other artificial nonsense.
Naming of functions and variables plays a critical role in proper modularization of your code. A properly named subroutine/function will tell you what should and should not be included. Same with variables—proper naming leads so natural normalization, that is, using the variable to contain only one piece of data not requiring later manipulation to extract what is needed.
In your case a speculative set of names might be:
Enroll (for adding a user)
Remove (for removing any object e.g.: user, token, door)
Door (a door)
Reader (a reader)
Schedule (an access schedule)
&c
Once you understand the objects (this does not require OOP, though that can be helpful) involved and they have names that most closely represent their encapsulated reality, you can move on to defining functions, again, based on this same idea. Real world analogies are very helpful. Overly abstract—or literal—functions will lead to confusion when new functionality is requested while well chosen abstractions will ofter already support that functionality requiring little or no change to make it available.
Naming is more important that a neophyte programmer is likely to grasp, but my most successful projects have also tracked my most success design efforts—especially naming.

